diff --git a/.gitignore b/.gitignore
index 9c1b8581d..18532844f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -68,4 +68,10 @@ examples/upscale_*
examples/grdecldips
data
steadystate_test_implicit
-steadystate_test_explicit
\ No newline at end of file
+steadystate_test_explicit
+=======
+*.iml
+.idea
+*/opm-simulation
+
+
diff --git a/examples/sim_poly_fi2p_comp_ad.cpp b/examples/sim_poly_fi2p_comp_ad.cpp
new file mode 100644
index 000000000..53220be7f
--- /dev/null
+++ b/examples/sim_poly_fi2p_comp_ad.cpp
@@ -0,0 +1,225 @@
+/*
+ Copyright 2014 SINTEF ICT, Applied Mathematics.
+ Copyright 2014 Statoil ASA.
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#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
+{
+ 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;
+
+ std::cout << "\n================ Test program for fully implicit three-phase black-oil flow ===============\n\n";
+ parameter::ParameterGroup param(argc, argv, false);
+ std::cout << "--------------- Reading parameters ---------------" << std::endl;
+
+ // If we have a "deck_filename", grid and props will be read from that.
+ bool use_deck = param.has("deck_filename");
+ if (!use_deck) {
+ OPM_THROW(std::runtime_error, "This program must be run with an input deck. "
+ "Specify the deck with deck_filename=deckname.data (for example).");
+ }
+ std::shared_ptr grid;
+ std::shared_ptr props;
+ std::shared_ptr new_props;
+ std::shared_ptr rock_comp;
+ PolymerBlackoilState state;
+ // bool check_well_controls = false;
+ // int max_well_control_iterations = 0;
+ double gravity[3] = { 0.0 };
+ std::string deck_filename = param.get("deck_filename");
+
+ Opm::ParserPtr newParser(new Opm::Parser());
+ bool strict_parsing = param.getDefault("strict_parsing", true);
+ Opm::DeckConstPtr deck = newParser->parseFile(deck_filename, strict_parsing);
+ std::shared_ptr eclipseState(new EclipseState(deck));
+ // Grid init
+ std::vector porv;
+ if (eclipseState->hasDoubleGridProperty("PORV")) {
+ porv = eclipseState->getDoubleGridProperty("PORV")->getData();
+ }
+ grid.reset(new GridManager(eclipseState->getEclipseGrid(), porv));
+ auto &cGrid = *grid->c_grid();
+ const PhaseUsage pu = Opm::phaseUsageFromDeck(deck);
+ Opm::EclipseWriter outputWriter(param,
+ eclipseState,
+ pu,
+ cGrid.number_of_cells,
+ cGrid.global_cell);
+ // Rock and fluid init
+ props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid(), param));
+ new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, *grid->c_grid()));
+ PolymerProperties polymer_props(deck, eclipseState);
+ PolymerPropsAd polymer_props_ad(polymer_props);
+ // Rock compressibility.
+ rock_comp.reset(new RockCompressibility(deck, eclipseState));
+ // Gravity.
+ gravity[2] = deck->hasKeyword("NOGRAV") ? 0.0 : unit::gravity;
+ // Init state variables (saturation and pressure).
+ if (param.has("init_saturation")) {
+ initStateBasic(*grid->c_grid(), *props, param, gravity[2], state);
+ initBlackoilSurfvol(*grid->c_grid(), *props, state);
+ } else {
+ initStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state);
+ }
+
+ bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0);
+ const double *grav = use_gravity ? &gravity[0] : 0;
+ // Solver for Newton iterations.
+ std::unique_ptr fis_solver;
+ if (param.getDefault("use_cpr", true)) {
+ fis_solver.reset(new NewtonIterationBlackoilCPR(param));
+ } else {
+ fis_solver.reset(new NewtonIterationBlackoilSimple(param));
+ }
+
+ // Write parameters used for later reference.
+ bool output = param.getDefault("output", true);
+ std::string output_dir;
+ if (output) {
+ output_dir =
+ param.getDefault("output_dir", std::string("output"));
+ boost::filesystem::path fpath(output_dir);
+ try {
+ create_directories(fpath);
+ }
+ catch (...) {
+ OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
+ }
+ param.writeParam(output_dir + "/simulation.param");
+ }
+
+ Opm::TimeMapConstPtr timeMap(eclipseState->getSchedule()->getTimeMap());
+ SimulatorTimer simtimer;
+ simtimer.init(timeMap);
+
+
+ SimulatorReport rep;
+ // With a deck, we may have more epochs etc.
+ WellState well_state;
+ // Check for WPOLYMER presence in last epoch to decide
+ // polymer injection control type.
+ const bool use_wpolymer = deck->hasKeyword("WPOLYMER");
+ if (use_wpolymer) {
+ if (param.has("poly_start_days")) {
+ OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck. "
+ "You seem to be trying to control it via parameter poly_start_days (etc.) as well.");
+ }
+ }
+ std::cout << "\n\n================ Starting main simulation loop ===============\n"
+ << std::flush;
+ SimulatorReport fullReport;
+ // Create and run simulator.
+ Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav);
+ SimulatorFullyImplicitCompressiblePolymer simulator(param,
+ *grid->c_grid(),
+ geology,
+ *new_props,
+ polymer_props_ad,
+ rock_comp->isActive() ? rock_comp.get() : 0,
+ eclipseState,
+ outputWriter,
+ deck,
+ *fis_solver,
+ grav);
+ fullReport= simulator.run(simtimer, state);
+
+ std::cout << "\n\n================ End of simulation ===============\n\n";
+ fullReport.report(std::cout);
+
+ if (output) {
+ std::string filename = output_dir + "/walltime.param";
+ std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out);
+ fullReport.reportParam(tot_os);
+ warnIfUnusedParams(param);
+ }
+
+}
+catch (const std::exception &e) {
+ std::cerr << "Program threw an exception: " << e.what() << "\n";
+ throw;
+}
+
diff --git a/examples/sim_poly_fibo_ad.cpp b/examples/sim_poly_fibo_ad.cpp
new file mode 100644
index 000000000..3f1536a16
--- /dev/null
+++ b/examples/sim_poly_fibo_ad.cpp
@@ -0,0 +1,253 @@
+/*
+ Copyright 2014 SINTEF ICT, Applied Mathematics.
+ Copyright 2014 STATOIL ASA.
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+#include "config.h"
+
+#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
+{
+ 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;
+
+ std::cout << "\n================ Test program for fully implicit three-phase black-oil flow ===============\n\n";
+ parameter::ParameterGroup param(argc, argv, false);
+ std::cout << "--------------- Reading parameters ---------------" << std::endl;
+
+ // If we have a "deck_filename", grid and props will be read from that.
+ bool use_deck = param.has("deck_filename");
+ if (!use_deck) {
+ OPM_THROW(std::runtime_error, "This program must be run with an input deck. "
+ "Specify the deck with deck_filename=deckname.data (for example).");
+ }
+ std::shared_ptr grid;
+ std::shared_ptr props;
+ std::shared_ptr new_props;
+ std::shared_ptr rock_comp;
+ PolymerBlackoilState state;
+ // bool check_well_controls = false;
+ // int max_well_control_iterations = 0;
+ double gravity[3] = { 0.0 };
+ std::string deck_filename = param.get("deck_filename");
+
+ Opm::ParserPtr newParser(new Opm::Parser() );
+ bool strict_parsing = param.getDefault("strict_parsing", true);
+ Opm::DeckConstPtr deck = newParser->parseFile(deck_filename, strict_parsing);
+ std::shared_ptr eclipseState(new EclipseState(deck));
+
+ // Grid init
+ std::vector porv;
+ if (eclipseState->hasDoubleGridProperty("PORV")) {
+ porv = eclipseState->getDoubleGridProperty("PORV")->getData();
+ }
+ grid.reset(new GridManager(eclipseState->getEclipseGrid(), porv));
+ auto &cGrid = *grid->c_grid();
+ const PhaseUsage pu = Opm::phaseUsageFromDeck(deck);
+ Opm::EclipseWriter outputWriter(param,
+ eclipseState,
+ pu,
+ cGrid.number_of_cells,
+ cGrid.global_cell);
+
+ // Rock and fluid init
+ props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid(), param));
+ new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, *grid->c_grid()));
+ const bool polymer = deck->hasKeyword("POLYMER");
+ const bool use_wpolymer = deck->hasKeyword("WPOLYMER");
+ PolymerProperties polymer_props(deck, eclipseState);
+ PolymerPropsAd polymer_props_ad(polymer_props);
+ // check_well_controls = param.getDefault("check_well_controls", false);
+ // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10);
+ // Rock compressibility.
+ rock_comp.reset(new RockCompressibility(deck, eclipseState));
+
+ // Gravity.
+ gravity[2] = deck->hasKeyword("NOGRAV") ? 0.0 : unit::gravity;
+
+ // Init state variables (saturation and pressure).
+ if (param.has("init_saturation")) {
+ initStateBasic(*grid->c_grid(), *props, param, gravity[2], state);
+ initBlackoilSurfvol(*grid->c_grid(), *props, state);
+ enum { Oil = BlackoilPhases::Liquid, Gas = BlackoilPhases::Vapour };
+ if (pu.phase_used[Oil] && pu.phase_used[Gas]) {
+ const int np = props->numPhases();
+ const int nc = grid->c_grid()->number_of_cells;
+ for (int c = 0; c < nc; ++c) {
+ state.gasoilratio()[c] = state.surfacevol()[c*np + pu.phase_pos[Gas]]
+ / state.surfacevol()[c*np + pu.phase_pos[Oil]];
+ }
+ }
+ } else if (deck->hasKeyword("EQUIL") && props->numPhases() == 3) {
+ state.init(*grid->c_grid(), props->numPhases());
+ const double grav = param.getDefault("gravity", unit::gravity);
+ initStateEquil(*grid->c_grid(), *props, deck, eclipseState, grav, state.blackoilState());
+ state.faceflux().resize(grid->c_grid()->number_of_faces, 0.0);
+ } else {
+ state.init(*grid->c_grid(), props->numPhases());
+ initBlackoilStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state.blackoilState());
+ }
+
+ bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0);
+ const double *grav = use_gravity ? &gravity[0] : 0;
+
+ // Solver for Newton iterations.
+ std::unique_ptr fis_solver;
+ if (param.getDefault("use_cpr", true)) {
+ fis_solver.reset(new NewtonIterationBlackoilCPR(param));
+ } else {
+ fis_solver.reset(new NewtonIterationBlackoilSimple(param));
+ }
+
+ // Write parameters used for later reference.
+ bool output = 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 (...) {
+ OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
+ }
+ // Write simulation parameters.
+ param.writeParam(output_dir + "/simulation.param");
+ }
+
+ Opm::TimeMapConstPtr timeMap(eclipseState->getSchedule()->getTimeMap());
+ SimulatorTimer simtimer;
+
+ // initialize variables
+ simtimer.init(timeMap);
+ if (polymer){
+ if (!use_wpolymer) {
+ OPM_MESSAGE("Warning: simulate polymer injection without WPOLYMER.");
+ } else {
+ if (param.has("polymer_start_days")) {
+ OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck."
+ "You seem to be trying to control it via parameter poly_start_days (etc.) as well.");
+ }
+ }
+ } else {
+ if (use_wpolymer) {
+ OPM_MESSAGE("Warning: use WPOLYMER in a non-polymer scenario.");
+ }
+ }
+ std::cout << "\n\n================ Starting main simulation loop ===============\n"
+ << std::flush;
+ SimulatorReport fullReport;
+ Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav);
+
+ std::vector threshold_pressures = thresholdPressures(deck, eclipseState, *grid->c_grid());
+ SimulatorFullyImplicitBlackoilPolymer simulator(param,
+ *grid->c_grid(),
+ geology,
+ *new_props,
+ polymer_props_ad,
+ rock_comp->isActive() ? rock_comp.get() : 0,
+ *fis_solver,
+ grav,
+ deck->hasKeyword("DISGAS"),
+ deck->hasKeyword("VAPOIL"),
+ polymer,
+ eclipseState,
+ outputWriter,
+ deck,
+ threshold_pressures);
+
+
+ fullReport = simulator.run(simtimer, state);
+ std::cout << "\n\n================ End of simulation ===============\n\n";
+ fullReport.report(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);
+ }
+}
+catch (const std::exception &e) {
+ std::cerr << "Program threw an exception: " << e.what() << "\n";
+ throw;
+}
+
diff --git a/opm/polymer/PolymerBlackoilState.hpp b/opm/polymer/PolymerBlackoilState.hpp
index 8116ec2c8..078ac27b2 100644
--- a/opm/polymer/PolymerBlackoilState.hpp
+++ b/opm/polymer/PolymerBlackoilState.hpp
@@ -44,6 +44,10 @@ namespace Opm
concentration_.resize(number_of_cells, 0.0);
cmax_.resize(number_of_cells, 0.0);
}
+ int numPhases() const
+ {
+ return state_blackoil_.numPhases();
+ }
enum ExtremalSat { MinSat = BlackoilState::MinSat, MaxSat = BlackoilState::MaxSat };
@@ -61,6 +65,8 @@ namespace Opm
std::vector& facepressure() { return state_blackoil_.facepressure(); }
std::vector& faceflux () { return state_blackoil_.faceflux(); }
std::vector& saturation () { return state_blackoil_.saturation(); }
+ std::vector& gasoilratio () { return state_blackoil_.gasoilratio(); }
+ std::vector& rv () { return state_blackoil_.rv(); }
std::vector& concentration() { return concentration_; }
std::vector& maxconcentration() { return cmax_; }
@@ -69,6 +75,8 @@ namespace Opm
const std::vector& facepressure() const { return state_blackoil_.facepressure(); }
const std::vector& faceflux () const { return state_blackoil_.faceflux(); }
const std::vector& saturation () const { return state_blackoil_.saturation(); }
+ const std::vector& gasoilratio() const { return state_blackoil_.gasoilratio(); }
+ const std::vector& rv () const { return state_blackoil_.rv(); }
const std::vector& concentration() const { return concentration_; }
const std::vector& maxconcentration() const { return cmax_; }
diff --git a/opm/polymer/PolymerState.hpp b/opm/polymer/PolymerState.hpp
index d5928edf9..d2374fb26 100644
--- a/opm/polymer/PolymerState.hpp
+++ b/opm/polymer/PolymerState.hpp
@@ -55,6 +55,10 @@ namespace Opm
state2p_.setFirstSat(cells, props, static_cast(es));
}
+ inline int numPhases() const
+ {
+ return state2p_.numPhases();
+ }
std::vector& pressure () { return state2p_.pressure(); }
std::vector& facepressure() { return state2p_.facepressure(); }
std::vector& faceflux () { return state2p_.faceflux(); }
@@ -71,7 +75,7 @@ namespace Opm
TwophaseState& twophaseState() { return state2p_; }
const TwophaseState& twophaseState() const { return state2p_; }
-
+
private:
TwophaseState state2p_;
std::vector concentration_;
diff --git a/opm/polymer/SimulatorCompressiblePolymer.cpp b/opm/polymer/SimulatorCompressiblePolymer.cpp
index 544543d78..dbb2efe81 100644
--- a/opm/polymer/SimulatorCompressiblePolymer.cpp
+++ b/opm/polymer/SimulatorCompressiblePolymer.cpp
@@ -292,207 +292,206 @@ namespace Opm
wellreport.push(props_, *wells_, state.pressure(), state.surfacevol(),
state.saturation(), 0.0, well_state.bhp(), well_state.perfRates());
}
- for (; !timer.done(); ++timer) {
- // Report timestep and (optionally) write state to disk.
- timer.report(std::cout);
- if (output_ && (timer.currentStepNum() % output_interval_ == 0)) {
- if (output_vtk_) {
- outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_);
+ // Report timestep and (optionally) write state to disk.
+ timer.report(std::cout);
+ if (output_ && (timer.currentStepNum() % output_interval_ == 0)) {
+ if (output_vtk_) {
+ outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_);
+ }
+ outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_);
+ }
+
+ initial_pressure = state.pressure();
+
+ // Solve pressure equation.
+ if (check_well_controls_) {
+ computeFractionalFlow(props_, poly_props_, allcells_,
+ state.pressure(), state.surfacevol(), state.saturation(),
+ state.concentration(), state.maxconcentration(),
+ fractional_flows);
+ wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase);
+ }
+ bool well_control_passed = !check_well_controls_;
+ int well_control_iteration = 0;
+ do {
+ // Run solver
+ pressure_timer.start();
+ psolver_.solve(timer.currentStepLength(), state, well_state);
+
+ // Renormalize pressure if both fluids and rock are
+ // incompressible, and there are no pressure
+ // conditions (bcs or wells). It is deemed sufficient
+ // for now to renormalize using geometric volume
+ // instead of pore volume.
+ if (psolver_.singularPressure()) {
+ // Compute average pressures of previous and last
+ // step, and total volume.
+ double av_prev_press = 0.0;
+ double av_press = 0.0;
+ double tot_vol = 0.0;
+ const int num_cells = grid_.number_of_cells;
+ for (int cell = 0; cell < num_cells; ++cell) {
+ av_prev_press += initial_pressure[cell]*grid_.cell_volumes[cell];
+ av_press += state.pressure()[cell]*grid_.cell_volumes[cell];
+ tot_vol += grid_.cell_volumes[cell];
+ }
+ // Renormalization constant
+ const double ren_const = (av_prev_press - av_press)/tot_vol;
+ for (int cell = 0; cell < num_cells; ++cell) {
+ state.pressure()[cell] += ren_const;
+ }
+ const int num_wells = (wells_ == NULL) ? 0 : wells_->number_of_wells;
+ for (int well = 0; well < num_wells; ++well) {
+ well_state.bhp()[well] += ren_const;
}
- outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_);
}
- initial_pressure = state.pressure();
+ // Stop timer and report
+ pressure_timer.stop();
+ double pt = pressure_timer.secsSinceStart();
+ std::cout << "Pressure solver took: " << pt << " seconds." << std::endl;
+ ptime += pt;
- // Solve pressure equation.
+ // Optionally, check if well controls are satisfied.
if (check_well_controls_) {
- computeFractionalFlow(props_, poly_props_, allcells_,
- state.pressure(), state.surfacevol(), state.saturation(),
- state.concentration(), state.maxconcentration(),
- fractional_flows);
- wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase);
- }
- bool well_control_passed = !check_well_controls_;
- int well_control_iteration = 0;
- do {
- // Run solver
- pressure_timer.start();
- psolver_.solve(timer.currentStepLength(), state, well_state);
-
- // Renormalize pressure if both fluids and rock are
- // incompressible, and there are no pressure
- // conditions (bcs or wells). It is deemed sufficient
- // for now to renormalize using geometric volume
- // instead of pore volume.
- if (psolver_.singularPressure()) {
- // Compute average pressures of previous and last
- // step, and total volume.
- double av_prev_press = 0.0;
- double av_press = 0.0;
- double tot_vol = 0.0;
- const int num_cells = grid_.number_of_cells;
- for (int cell = 0; cell < num_cells; ++cell) {
- av_prev_press += initial_pressure[cell]*grid_.cell_volumes[cell];
- av_press += state.pressure()[cell]*grid_.cell_volumes[cell];
- tot_vol += grid_.cell_volumes[cell];
- }
- // Renormalization constant
- const double ren_const = (av_prev_press - av_press)/tot_vol;
- for (int cell = 0; cell < num_cells; ++cell) {
- state.pressure()[cell] += ren_const;
- }
- const int num_wells = (wells_ == NULL) ? 0 : wells_->number_of_wells;
- for (int well = 0; well < num_wells; ++well) {
- well_state.bhp()[well] += ren_const;
- }
+ Opm::computePhaseFlowRatesPerWell(*wells_,
+ well_state.perfRates(),
+ fractional_flows,
+ well_resflows_phase);
+ std::cout << "Checking well conditions." << std::endl;
+ // For testing we set surface := reservoir
+ well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase);
+ ++well_control_iteration;
+ if (!well_control_passed && well_control_iteration > max_well_control_iterations_) {
+ OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries.");
}
-
- // Stop timer and report
- pressure_timer.stop();
- double pt = pressure_timer.secsSinceStart();
- std::cout << "Pressure solver took: " << pt << " seconds." << std::endl;
- ptime += pt;
-
- // Optionally, check if well controls are satisfied.
- if (check_well_controls_) {
- Opm::computePhaseFlowRatesPerWell(*wells_,
- well_state.perfRates(),
- fractional_flows,
- well_resflows_phase);
- std::cout << "Checking well conditions." << std::endl;
- // For testing we set surface := reservoir
- well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase);
- ++well_control_iteration;
- if (!well_control_passed && well_control_iteration > max_well_control_iterations_) {
- OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries.");
- }
- if (!well_control_passed) {
- std::cout << "Well controls not passed, solving again." << std::endl;
- } else {
- std::cout << "Well conditions met." << std::endl;
- }
- }
- } while (!well_control_passed);
-
- // Update pore volumes if rock is compressible.
- if (rock_comp_props_ && rock_comp_props_->isActive()) {
- initial_porevol = porevol;
- computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
- }
-
- // Process transport sources (to include bdy terms and well flows).
- Opm::computeTransportSource(props_, wells_, well_state, transport_src);
-
- // Find inflow rate.
- const double current_time = timer.simulationTimeElapsed();
- double stepsize = timer.currentStepLength();
- polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c);
-
- // Solve transport.
- transport_timer.start();
- if (num_transport_substeps_ != 1) {
- stepsize /= double(num_transport_substeps_);
- std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl;
- }
- double injected[2] = { 0.0 };
- double produced[2] = { 0.0 };
- double polyinj = 0.0;
- double polyprod = 0.0;
- for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) {
- tsolver_.solve(&state.faceflux()[0], initial_pressure,
- state.pressure(), &initial_porevol[0], &porevol[0],
- &transport_src[0], &polymer_inflow_c[0], stepsize,
- state.saturation(), state.surfacevol(),
- state.concentration(), state.maxconcentration());
- double substep_injected[2] = { 0.0 };
- double substep_produced[2] = { 0.0 };
- double substep_polyinj = 0.0;
- double substep_polyprod = 0.0;
- Opm::computeInjectedProduced(props_, poly_props_,
- state,
- transport_src, polymer_inflow_c, stepsize,
- substep_injected, substep_produced,
- substep_polyinj, substep_polyprod);
- injected[0] += substep_injected[0];
- injected[1] += substep_injected[1];
- produced[0] += substep_produced[0];
- produced[1] += substep_produced[1];
- polyinj += substep_polyinj;
- polyprod += substep_polyprod;
- if (gravity_ != 0 && use_segregation_split_) {
- tsolver_.solveGravity(columns_, stepsize,
- state.saturation(), state.surfacevol(),
- state.concentration(), state.maxconcentration());
+ if (!well_control_passed) {
+ std::cout << "Well controls not passed, solving again." << std::endl;
+ } else {
+ std::cout << "Well conditions met." << std::endl;
}
}
- transport_timer.stop();
- double tt = transport_timer.secsSinceStart();
- std::cout << "Transport solver took: " << tt << " seconds." << std::endl;
- ttime += tt;
+ } while (!well_control_passed);
- // Report volume balances.
- Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol);
- polymass = Opm::computePolymerMass(porevol, state.saturation(), state.concentration(), poly_props_.deadPoreVol());
- polymass_adsorbed = Opm::computePolymerAdsorbed(grid_, props_, poly_props_,
- state, rock_comp_props_);
- tot_injected[0] += injected[0];
- tot_injected[1] += injected[1];
- tot_produced[0] += produced[0];
- tot_produced[1] += produced[1];
- tot_polyinj += polyinj;
- tot_polyprod += polyprod;
- std::cout.precision(5);
- const int width = 18;
- std::cout << "\nMass balance: "
- " water(surfvol) oil(surfvol) polymer(kg)\n";
- std::cout << " In-place: "
- << std::setw(width) << inplace_surfvol[0]
- << std::setw(width) << inplace_surfvol[1]
- << std::setw(width) << polymass << std::endl;
- std::cout << " Adsorbed: "
- << std::setw(width) << 0.0
- << std::setw(width) << 0.0
- << std::setw(width) << polymass_adsorbed << std::endl;
- std::cout << " Injected: "
- << std::setw(width) << injected[0]
- << std::setw(width) << injected[1]
- << std::setw(width) << polyinj << std::endl;
- std::cout << " Produced: "
- << std::setw(width) << produced[0]
- << std::setw(width) << produced[1]
- << std::setw(width) << polyprod << std::endl;
- std::cout << " Total inj: "
- << std::setw(width) << tot_injected[0]
- << std::setw(width) << tot_injected[1]
- << std::setw(width) << tot_polyinj << std::endl;
- std::cout << " Total prod: "
- << std::setw(width) << tot_produced[0]
- << std::setw(width) << tot_produced[1]
- << std::setw(width) << tot_polyprod << std::endl;
- const double balance[3] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0],
- init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1],
- init_polymass - polymass - tot_polyprod + tot_polyinj - polymass_adsorbed };
- std::cout << " Initial - inplace + inj - prod: "
- << std::setw(width) << balance[0]
- << std::setw(width) << balance[1]
- << std::setw(width) << balance[2]
- << std::endl;
- std::cout << " Relative mass error: "
- << std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0])
- << std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1])
- << std::setw(width) << balance[2]/(init_polymass + tot_polyinj)
- << std::endl;
- std::cout.precision(8);
+ // Update pore volumes if rock is compressible.
+ if (rock_comp_props_ && rock_comp_props_->isActive()) {
+ initial_porevol = porevol;
+ computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
+ }
- watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(),
- produced[0]/(produced[0] + produced[1]),
- tot_produced[0]/tot_porevol_init);
- if (wells_) {
- wellreport.push(props_, *wells_, state.pressure(), state.surfacevol(),
- state.saturation(), timer.simulationTimeElapsed() + timer.currentStepLength(),
- well_state.bhp(), well_state.perfRates());
+ // Process transport sources (to include bdy terms and well flows).
+ Opm::computeTransportSource(props_, wells_, well_state, transport_src);
+
+ // Find inflow rate.
+ const double current_time = timer.simulationTimeElapsed();
+ double stepsize = timer.currentStepLength();
+ polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c);
+
+
+ // Solve transport.
+ transport_timer.start();
+ if (num_transport_substeps_ != 1) {
+ stepsize /= double(num_transport_substeps_);
+ std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl;
+ }
+ double injected[2] = { 0.0 };
+ double produced[2] = { 0.0 };
+ double polyinj = 0.0;
+ double polyprod = 0.0;
+ for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) {
+ tsolver_.solve(&state.faceflux()[0], initial_pressure,
+ state.pressure(), &initial_porevol[0], &porevol[0],
+ &transport_src[0], &polymer_inflow_c[0], stepsize,
+ state.saturation(), state.surfacevol(),
+ state.concentration(), state.maxconcentration());
+ double substep_injected[2] = { 0.0 };
+ double substep_produced[2] = { 0.0 };
+ double substep_polyinj = 0.0;
+ double substep_polyprod = 0.0;
+ Opm::computeInjectedProduced(props_, poly_props_,
+ state,
+ transport_src, polymer_inflow_c, stepsize,
+ substep_injected, substep_produced,
+ substep_polyinj, substep_polyprod);
+ injected[0] += substep_injected[0];
+ injected[1] += substep_injected[1];
+ produced[0] += substep_produced[0];
+ produced[1] += substep_produced[1];
+ polyinj += substep_polyinj;
+ polyprod += substep_polyprod;
+ if (gravity_ != 0 && use_segregation_split_) {
+ tsolver_.solveGravity(columns_, stepsize,
+ state.saturation(), state.surfacevol(),
+ state.concentration(), state.maxconcentration());
}
}
+ transport_timer.stop();
+ double tt = transport_timer.secsSinceStart();
+ std::cout << "Transport solver took: " << tt << " seconds." << std::endl;
+ ttime += tt;
+
+ // Report volume balances.
+ Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol);
+ polymass = Opm::computePolymerMass(porevol, state.saturation(), state.concentration(), poly_props_.deadPoreVol());
+ polymass_adsorbed = Opm::computePolymerAdsorbed(grid_, props_, poly_props_,
+ state, rock_comp_props_);
+ tot_injected[0] += injected[0];
+ tot_injected[1] += injected[1];
+ tot_produced[0] += produced[0];
+ tot_produced[1] += produced[1];
+ tot_polyinj += polyinj;
+ tot_polyprod += polyprod;
+ std::cout.precision(5);
+ const int width = 18;
+ std::cout << "\nMass balance: "
+ " water(surfvol) oil(surfvol) polymer(kg)\n";
+ std::cout << " In-place: "
+ << std::setw(width) << inplace_surfvol[0]
+ << std::setw(width) << inplace_surfvol[1]
+ << std::setw(width) << polymass << std::endl;
+ std::cout << " Adsorbed: "
+ << std::setw(width) << 0.0
+ << std::setw(width) << 0.0
+ << std::setw(width) << polymass_adsorbed << std::endl;
+ std::cout << " Injected: "
+ << std::setw(width) << injected[0]
+ << std::setw(width) << injected[1]
+ << std::setw(width) << polyinj << std::endl;
+ std::cout << " Produced: "
+ << std::setw(width) << produced[0]
+ << std::setw(width) << produced[1]
+ << std::setw(width) << polyprod << std::endl;
+ std::cout << " Total inj: "
+ << std::setw(width) << tot_injected[0]
+ << std::setw(width) << tot_injected[1]
+ << std::setw(width) << tot_polyinj << std::endl;
+ std::cout << " Total prod: "
+ << std::setw(width) << tot_produced[0]
+ << std::setw(width) << tot_produced[1]
+ << std::setw(width) << tot_polyprod << std::endl;
+ const double balance[3] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0],
+ init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1],
+ init_polymass - polymass - tot_polyprod + tot_polyinj - polymass_adsorbed };
+ std::cout << " Initial - inplace + inj - prod: "
+ << std::setw(width) << balance[0]
+ << std::setw(width) << balance[1]
+ << std::setw(width) << balance[2]
+ << std::endl;
+ std::cout << " Relative mass error: "
+ << std::setw(width) << balance[0]/(init_surfvol[0] + tot_injected[0])
+ << std::setw(width) << balance[1]/(init_surfvol[1] + tot_injected[1])
+ << std::setw(width) << balance[2]/(init_polymass + tot_polyinj)
+ << std::endl;
+ std::cout.precision(8);
+
+ watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(),
+ produced[0]/(produced[0] + produced[1]),
+ tot_produced[0]/tot_porevol_init);
+ if (wells_) {
+ wellreport.push(props_, *wells_, state.pressure(), state.surfacevol(),
+ state.saturation(), timer.simulationTimeElapsed() + timer.currentStepLength(),
+ well_state.bhp(), well_state.perfRates());
+ }
if (output_) {
if (output_vtk_) {
diff --git a/opm/polymer/SimulatorPolymer.cpp b/opm/polymer/SimulatorPolymer.cpp
index 827b80ddd..8d943d328 100644
--- a/opm/polymer/SimulatorPolymer.cpp
+++ b/opm/polymer/SimulatorPolymer.cpp
@@ -315,196 +315,194 @@ namespace Opm
well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0);
wellreport.push(props_, *wells_, state.saturation(), 0.0, well_state.bhp(), well_state.perfRates());
}
- for (; !timer.done(); ++timer) {
- // Report timestep and (optionally) write state to disk.
- timer.report(std::cout);
- if (output_ && (timer.currentStepNum() % output_interval_ == 0)) {
- if (output_vtk_) {
- outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_);
+ // Report timestep and (optionally) write state to disk.
+ timer.report(std::cout);
+ if (output_ && (timer.currentStepNum() % output_interval_ == 0)) {
+ if (output_vtk_) {
+ outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_);
+ }
+ if (output_binary_) {
+ outputStateBinary(grid_, state, timer, output_dir_);
+ }
+ outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_);
+ }
+
+ // Solve pressure.
+ if (check_well_controls_) {
+ computeFractionalFlow(props_, poly_props_, allcells_,
+ state.saturation(), state.concentration(), state.maxconcentration(),
+ fractional_flows);
+ wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase);
+ }
+ bool well_control_passed = !check_well_controls_;
+ int well_control_iteration = 0;
+ do {
+ // Run solver.
+ pressure_timer.start();
+ std::vector initial_pressure = state.pressure();
+ psolver_.solve(timer.currentStepLength(), state, well_state);
+
+ // Renormalize pressure if rock is incompressible, and
+ // there are no pressure conditions (bcs or wells).
+ // It is deemed sufficient for now to renormalize
+ // using geometric volume instead of pore volume.
+ if ((rock_comp_props_ == NULL || !rock_comp_props_->isActive())
+ && allNeumannBCs(bcs_) && allRateWells(wells_)) {
+ // Compute average pressures of previous and last
+ // step, and total volume.
+ double av_prev_press = 0.0;
+ double av_press = 0.0;
+ double tot_vol = 0.0;
+ const int num_cells = grid_.number_of_cells;
+ for (int cell = 0; cell < num_cells; ++cell) {
+ av_prev_press += initial_pressure[cell]*grid_.cell_volumes[cell];
+ av_press += state.pressure()[cell]*grid_.cell_volumes[cell];
+ tot_vol += grid_.cell_volumes[cell];
}
- if (output_binary_) {
- outputStateBinary(grid_, state, timer, output_dir_);
+ // Renormalization constant
+ const double ren_const = (av_prev_press - av_press)/tot_vol;
+ for (int cell = 0; cell < num_cells; ++cell) {
+ state.pressure()[cell] += ren_const;
+ }
+ const int num_wells = (wells_ == NULL) ? 0 : wells_->number_of_wells;
+ for (int well = 0; well < num_wells; ++well) {
+ well_state.bhp()[well] += ren_const;
}
- outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_);
}
- // Solve pressure.
+ // Stop timer and report.
+ pressure_timer.stop();
+ double pt = pressure_timer.secsSinceStart();
+ std::cout << "Pressure solver took: " << pt << " seconds." << std::endl;
+ ptime += pt;
+
+ // Optionally, check if well controls are satisfied.
if (check_well_controls_) {
- computeFractionalFlow(props_, poly_props_, allcells_,
- state.saturation(), state.concentration(), state.maxconcentration(),
- fractional_flows);
- wells_manager_.applyExplicitReinjectionControls(well_resflows_phase, well_resflows_phase);
- }
- bool well_control_passed = !check_well_controls_;
- int well_control_iteration = 0;
- do {
- // Run solver.
- pressure_timer.start();
- std::vector initial_pressure = state.pressure();
- psolver_.solve(timer.currentStepLength(), state, well_state);
-
- // Renormalize pressure if rock is incompressible, and
- // there are no pressure conditions (bcs or wells).
- // It is deemed sufficient for now to renormalize
- // using geometric volume instead of pore volume.
- if ((rock_comp_props_ == NULL || !rock_comp_props_->isActive())
- && allNeumannBCs(bcs_) && allRateWells(wells_)) {
- // Compute average pressures of previous and last
- // step, and total volume.
- double av_prev_press = 0.0;
- double av_press = 0.0;
- double tot_vol = 0.0;
- const int num_cells = grid_.number_of_cells;
- for (int cell = 0; cell < num_cells; ++cell) {
- av_prev_press += initial_pressure[cell]*grid_.cell_volumes[cell];
- av_press += state.pressure()[cell]*grid_.cell_volumes[cell];
- tot_vol += grid_.cell_volumes[cell];
- }
- // Renormalization constant
- const double ren_const = (av_prev_press - av_press)/tot_vol;
- for (int cell = 0; cell < num_cells; ++cell) {
- state.pressure()[cell] += ren_const;
- }
- const int num_wells = (wells_ == NULL) ? 0 : wells_->number_of_wells;
- for (int well = 0; well < num_wells; ++well) {
- well_state.bhp()[well] += ren_const;
- }
+ Opm::computePhaseFlowRatesPerWell(*wells_,
+ well_state.perfRates(),
+ fractional_flows,
+ well_resflows_phase);
+ std::cout << "Checking well conditions." << std::endl;
+ // For testing we set surface := reservoir
+ well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase);
+ ++well_control_iteration;
+ if (!well_control_passed && well_control_iteration > max_well_control_iterations_) {
+ OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries.");
}
-
- // Stop timer and report.
- pressure_timer.stop();
- double pt = pressure_timer.secsSinceStart();
- std::cout << "Pressure solver took: " << pt << " seconds." << std::endl;
- ptime += pt;
-
- // Optionally, check if well controls are satisfied.
- if (check_well_controls_) {
- Opm::computePhaseFlowRatesPerWell(*wells_,
- well_state.perfRates(),
- fractional_flows,
- well_resflows_phase);
- std::cout << "Checking well conditions." << std::endl;
- // For testing we set surface := reservoir
- well_control_passed = wells_manager_.conditionsMet(well_state.bhp(), well_resflows_phase, well_resflows_phase);
- ++well_control_iteration;
- if (!well_control_passed && well_control_iteration > max_well_control_iterations_) {
- OPM_THROW(std::runtime_error, "Could not satisfy well conditions in " << max_well_control_iterations_ << " tries.");
- }
- if (!well_control_passed) {
- std::cout << "Well controls not passed, solving again." << std::endl;
- } else {
- std::cout << "Well conditions met." << std::endl;
- }
- }
- } while (!well_control_passed);
-
- // Update pore volumes if rock is compressible.
- if (rock_comp_props_ && rock_comp_props_->isActive()) {
- initial_porevol = porevol;
- computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
- }
-
- // Process transport sources (to include bdy terms and well flows).
- Opm::computeTransportSource(grid_, src_, state.faceflux(), 1.0,
- wells_, well_state.perfRates(), transport_src);
-
- // Find inflow rate.
- const double current_time = timer.simulationTimeElapsed();
- double stepsize = timer.currentStepLength();
- polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c);
-
- // Solve transport.
- transport_timer.start();
- if (num_transport_substeps_ != 1) {
- stepsize /= double(num_transport_substeps_);
- std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl;
- }
- double substep_injected[2] = { 0.0 };
- double substep_produced[2] = { 0.0 };
- double substep_polyinj = 0.0;
- double substep_polyprod = 0.0;
- injected[0] = injected[1] = produced[0] = produced[1] = polyinj = polyprod = 0.0;
- for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) {
- tsolver_.solve(&state.faceflux()[0], &initial_porevol[0], &transport_src[0], &polymer_inflow_c[0], stepsize,
- state.saturation(), state.concentration(), state.maxconcentration());
- Opm::computeInjectedProduced(props_, poly_props_,
- state,
- transport_src, polymer_inflow_c, stepsize,
- substep_injected, substep_produced, substep_polyinj, substep_polyprod);
- injected[0] += substep_injected[0];
- injected[1] += substep_injected[1];
- produced[0] += substep_produced[0];
- produced[1] += substep_produced[1];
- polyinj += substep_polyinj;
- polyprod += substep_polyprod;
- if (use_segregation_split_) {
- tsolver_.solveGravity(columns_, &porevol[0], stepsize,
- state.saturation(), state.concentration(), state.maxconcentration());
+ if (!well_control_passed) {
+ std::cout << "Well controls not passed, solving again." << std::endl;
+ } else {
+ std::cout << "Well conditions met." << std::endl;
}
}
- transport_timer.stop();
- double tt = transport_timer.secsSinceStart();
- std::cout << "Transport solver took: " << tt << " seconds." << std::endl;
- ttime += tt;
+ } while (!well_control_passed);
- // Report volume balances.
- Opm::computeSaturatedVol(porevol, state.saturation(), satvol);
- polymass = Opm::computePolymerMass(porevol, state.saturation(), state.concentration(), poly_props_.deadPoreVol());
- polymass_adsorbed = Opm::computePolymerAdsorbed(props_, poly_props_, porevol, state.maxconcentration());
- tot_injected[0] += injected[0];
- tot_injected[1] += injected[1];
- tot_produced[0] += produced[0];
- tot_produced[1] += produced[1];
- tot_polyinj += polyinj;
- tot_polyprod += polyprod;
- std::cout.precision(5);
- const int width = 18;
- std::cout << "\nVolume and polymer mass balance: "
- " water(pv) oil(pv) polymer(kg)\n";
- std::cout << " Saturated volumes: "
- << std::setw(width) << satvol[0]/tot_porevol_init
- << std::setw(width) << satvol[1]/tot_porevol_init
- << std::setw(width) << polymass << std::endl;
- std::cout << " Adsorbed volumes: "
- << std::setw(width) << 0.0
- << std::setw(width) << 0.0
- << std::setw(width) << polymass_adsorbed << std::endl;
- std::cout << " Injected volumes: "
- << std::setw(width) << injected[0]/tot_porevol_init
- << std::setw(width) << injected[1]/tot_porevol_init
- << std::setw(width) << polyinj << std::endl;
- std::cout << " Produced volumes: "
- << std::setw(width) << produced[0]/tot_porevol_init
- << std::setw(width) << produced[1]/tot_porevol_init
- << std::setw(width) << polyprod << std::endl;
- std::cout << " Total inj volumes: "
- << std::setw(width) << tot_injected[0]/tot_porevol_init
- << std::setw(width) << tot_injected[1]/tot_porevol_init
- << std::setw(width) << tot_polyinj << std::endl;
- std::cout << " Total prod volumes: "
- << std::setw(width) << tot_produced[0]/tot_porevol_init
- << std::setw(width) << tot_produced[1]/tot_porevol_init
- << std::setw(width) << tot_polyprod << std::endl;
- std::cout << " In-place + prod - inj: "
- << std::setw(width) << (satvol[0] + tot_produced[0] - tot_injected[0])/tot_porevol_init
- << std::setw(width) << (satvol[1] + tot_produced[1] - tot_injected[1])/tot_porevol_init
- << std::setw(width) << (polymass + tot_polyprod - tot_polyinj + polymass_adsorbed) << std::endl;
- std::cout << " Init - now - pr + inj: "
- << std::setw(width) << (init_satvol[0] - satvol[0] - tot_produced[0] + tot_injected[0])/tot_porevol_init
- << std::setw(width) << (init_satvol[1] - satvol[1] - tot_produced[1] + tot_injected[1])/tot_porevol_init
- << std::setw(width) << (init_polymass - polymass - tot_polyprod + tot_polyinj - polymass_adsorbed)
- << std::endl;
- std::cout.precision(8);
+ // Update pore volumes if rock is compressible.
+ if (rock_comp_props_ && rock_comp_props_->isActive()) {
+ initial_porevol = porevol;
+ computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
+ }
- watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(),
- produced[0]/(produced[0] + produced[1]),
- tot_produced[0]/tot_porevol_init);
- if (wells_) {
- wellreport.push(props_, *wells_, state.saturation(),
- timer.simulationTimeElapsed() + timer.currentStepLength(),
- well_state.bhp(), well_state.perfRates());
+ // Process transport sources (to include bdy terms and well flows).
+ Opm::computeTransportSource(grid_, src_, state.faceflux(), 1.0,
+ wells_, well_state.perfRates(), transport_src);
+
+ // Find inflow rate.
+ const double current_time = timer.simulationTimeElapsed();
+ double stepsize = timer.currentStepLength();
+ polymer_inflow_.getInflowValues(current_time, current_time + stepsize, polymer_inflow_c);
+
+ // Solve transport.
+ transport_timer.start();
+ if (num_transport_substeps_ != 1) {
+ stepsize /= double(num_transport_substeps_);
+ std::cout << "Making " << num_transport_substeps_ << " transport substeps." << std::endl;
+ }
+ double substep_injected[2] = { 0.0 };
+ double substep_produced[2] = { 0.0 };
+ double substep_polyinj = 0.0;
+ double substep_polyprod = 0.0;
+ injected[0] = injected[1] = produced[0] = produced[1] = polyinj = polyprod = 0.0;
+ for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) {
+ tsolver_.solve(&state.faceflux()[0], &initial_porevol[0], &transport_src[0], &polymer_inflow_c[0], stepsize,
+ state.saturation(), state.concentration(), state.maxconcentration());
+ Opm::computeInjectedProduced(props_, poly_props_,
+ state,
+ transport_src, polymer_inflow_c, stepsize,
+ substep_injected, substep_produced, substep_polyinj, substep_polyprod);
+ injected[0] += substep_injected[0];
+ injected[1] += substep_injected[1];
+ produced[0] += substep_produced[0];
+ produced[1] += substep_produced[1];
+ polyinj += substep_polyinj;
+ polyprod += substep_polyprod;
+ if (use_segregation_split_) {
+ tsolver_.solveGravity(columns_, &porevol[0], stepsize,
+ state.saturation(), state.concentration(), state.maxconcentration());
}
}
+ transport_timer.stop();
+ double tt = transport_timer.secsSinceStart();
+ std::cout << "Transport solver took: " << tt << " seconds." << std::endl;
+ ttime += tt;
+
+ // Report volume balances.
+ Opm::computeSaturatedVol(porevol, state.saturation(), satvol);
+ polymass = Opm::computePolymerMass(porevol, state.saturation(), state.concentration(), poly_props_.deadPoreVol());
+ polymass_adsorbed = Opm::computePolymerAdsorbed(props_, poly_props_, porevol, state.maxconcentration());
+ tot_injected[0] += injected[0];
+ tot_injected[1] += injected[1];
+ tot_produced[0] += produced[0];
+ tot_produced[1] += produced[1];
+ tot_polyinj += polyinj;
+ tot_polyprod += polyprod;
+ std::cout.precision(5);
+ const int width = 18;
+ std::cout << "\nVolume and polymer mass balance: "
+ " water(pv) oil(pv) polymer(kg)\n";
+ std::cout << " Saturated volumes: "
+ << std::setw(width) << satvol[0]/tot_porevol_init
+ << std::setw(width) << satvol[1]/tot_porevol_init
+ << std::setw(width) << polymass << std::endl;
+ std::cout << " Adsorbed volumes: "
+ << std::setw(width) << 0.0
+ << std::setw(width) << 0.0
+ << std::setw(width) << polymass_adsorbed << std::endl;
+ std::cout << " Injected volumes: "
+ << std::setw(width) << injected[0]/tot_porevol_init
+ << std::setw(width) << injected[1]/tot_porevol_init
+ << std::setw(width) << polyinj << std::endl;
+ std::cout << " Produced volumes: "
+ << std::setw(width) << produced[0]/tot_porevol_init
+ << std::setw(width) << produced[1]/tot_porevol_init
+ << std::setw(width) << polyprod << std::endl;
+ std::cout << " Total inj volumes: "
+ << std::setw(width) << tot_injected[0]/tot_porevol_init
+ << std::setw(width) << tot_injected[1]/tot_porevol_init
+ << std::setw(width) << tot_polyinj << std::endl;
+ std::cout << " Total prod volumes: "
+ << std::setw(width) << tot_produced[0]/tot_porevol_init
+ << std::setw(width) << tot_produced[1]/tot_porevol_init
+ << std::setw(width) << tot_polyprod << std::endl;
+ std::cout << " In-place + prod - inj: "
+ << std::setw(width) << (satvol[0] + tot_produced[0] - tot_injected[0])/tot_porevol_init
+ << std::setw(width) << (satvol[1] + tot_produced[1] - tot_injected[1])/tot_porevol_init
+ << std::setw(width) << (polymass + tot_polyprod - tot_polyinj + polymass_adsorbed) << std::endl;
+ std::cout << " Init - now - pr + inj: "
+ << std::setw(width) << (init_satvol[0] - satvol[0] - tot_produced[0] + tot_injected[0])/tot_porevol_init
+ << std::setw(width) << (init_satvol[1] - satvol[1] - tot_produced[1] + tot_injected[1])/tot_porevol_init
+ << std::setw(width) << (init_polymass - polymass - tot_polyprod + tot_polyinj - polymass_adsorbed)
+ << std::endl;
+ std::cout.precision(8);
+
+ watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(),
+ produced[0]/(produced[0] + produced[1]),
+ tot_produced[0]/tot_porevol_init);
+ if (wells_) {
+ wellreport.push(props_, *wells_, state.saturation(),
+ timer.simulationTimeElapsed() + timer.currentStepLength(),
+ well_state.bhp(), well_state.perfRates());
+ }
if (output_) {
if (output_vtk_) {
diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp
new file mode 100644
index 000000000..ab3e3f38c
--- /dev/null
+++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp
@@ -0,0 +1,372 @@
+/*
+ Copyright 2013 SINTEF ICT, Applied Mathematics.
+ Copyright 2014 STATOIL ASA.
+
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+
+#ifndef OPM_FULLYIMPLICITBLACKOILPOLYMERSOLVER_HEADER_INCLUDED
+#define OPM_FULLYIMPLICITBLACKOILPOLYMERSOLVER_HEADER_INCLUDED
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct UnstructuredGrid;
+struct Wells;
+
+namespace Opm {
+
+ namespace parameter { class ParameterGroup; }
+ class DerivedGeology;
+ class RockCompressibility;
+ class NewtonIterationBlackoilInterface;
+ class PolymerBlackoilState;
+ class WellStateFullyImplicitBlackoil;
+
+
+ /// A fully implicit solver for the black-oil-polymer problem.
+ ///
+ /// The simulator is capable of handling three-phase problems
+ /// where gas can be dissolved in oil (but not vice versa). It
+ /// uses an industry-standard TPFA discretization with per-phase
+ /// upwind weighting of mobilities.
+ ///
+ /// It uses automatic differentiation via the class AutoDiffBlock
+ /// to simplify assembly of the jacobian matrix.
+ template
+ class FullyImplicitBlackoilPolymerSolver
+ {
+ public:
+ /// \brief The type of the grid that we use.
+ typedef T Grid;
+ /// Construct a solver. It will retain references to the
+ /// arguments of this functions, and they are expected to
+ /// remain in scope for the lifetime of the solver.
+ /// \param[in] param parameters
+ /// \param[in] grid grid data structure
+ /// \param[in] fluid fluid properties
+ /// \param[in] geo rock properties
+ /// \param[in] rock_comp_props if non-null, rock compressibility properties
+ /// \param[in] wells well structure
+ /// \param[in] linsolver linear solver
+ FullyImplicitBlackoilPolymerSolver(const parameter::ParameterGroup& param,
+ const Grid& grid ,
+ const BlackoilPropsAdInterface& fluid,
+ const DerivedGeology& geo ,
+ const RockCompressibility* rock_comp_props,
+ const PolymerPropsAd& polymer_props_ad,
+ const Wells& wells,
+ const NewtonIterationBlackoilInterface& linsolver,
+ const bool has_disgas,
+ const bool has_vapoil,
+ const bool has_polymer);
+
+ /// \brief Set threshold pressures that prevent or reduce flow.
+ /// This prevents flow across faces if the potential
+ /// difference is less than the threshold. If the potential
+ /// difference is greater, the threshold value is subtracted
+ /// before calculating flow. This is treated symmetrically, so
+ /// flow is prevented or reduced in both directions equally.
+ /// \param[in] threshold_pressures_by_face array of size equal to the number of faces
+ /// of the grid passed in the constructor.
+ void setThresholdPressures(const std::vector& threshold_pressures_by_face);
+
+ /// Take a single forward step, modifiying
+ /// state.pressure()
+ /// state.faceflux()
+ /// state.saturation()
+ /// state.gasoilratio()
+ /// wstate.bhp()
+ /// \param[in] dt time step size
+ /// \param[in] state reservoir state
+ /// \param[in] wstate well state
+ void
+ step(const double dt ,
+ PolymerBlackoilState& state ,
+ WellStateFullyImplicitBlackoil& wstate,
+ const std::vector& polymer_inflow);
+
+ private:
+ // Types and enums
+ typedef AutoDiffBlock ADB;
+ typedef ADB::V V;
+ typedef ADB::M M;
+ typedef Eigen::Array DataBlock;
+
+ struct ReservoirResidualQuant {
+ ReservoirResidualQuant();
+ std::vector accum; // Accumulations
+ ADB mflux; // Mass flux (surface conditions)
+ ADB b; // Reciprocal FVF
+ ADB head; // Pressure drop across int. interfaces
+ ADB mob; // Phase mobility (per cell)
+ };
+
+ struct SolutionState {
+ SolutionState(const int np);
+ ADB pressure;
+ std::vector saturation;
+ ADB rs;
+ ADB rv;
+ ADB concentration;
+ ADB qs;
+ ADB bhp;
+ };
+
+ struct WellOps {
+ WellOps(const Wells& wells);
+ M w2p; // well -> perf (scatter)
+ M p2w; // perf -> well (gather)
+ };
+
+ enum { Water = BlackoilPropsAdInterface::Water,
+ Oil = BlackoilPropsAdInterface::Oil ,
+ Gas = BlackoilPropsAdInterface::Gas };
+
+ // the Newton relaxation type
+ enum RelaxType { DAMPEN, SOR };
+ enum PrimalVariables { Sg = 0, RS = 1, RV = 2 };
+
+ // Member data
+ const Grid& grid_;
+ const BlackoilPropsAdInterface& fluid_;
+ const DerivedGeology& geo_;
+ const RockCompressibility* rock_comp_props_;
+ const PolymerPropsAd& polymer_props_ad_;
+ const Wells& wells_;
+ const NewtonIterationBlackoilInterface& linsolver_;
+ // For each canonical phase -> true if active
+ const std::vector active_;
+ // Size = # active phases. Maps active -> canonical phase indices.
+ const std::vector canph_;
+ const std::vector cells_; // All grid cells
+ HelperOps ops_;
+ const WellOps wops_;
+ V cmax_;
+ const bool has_disgas_;
+ const bool has_vapoil_;
+ const bool has_polymer_;
+ const int poly_pos_;
+ double dp_max_rel_;
+ double ds_max_;
+ double drs_max_rel_;
+ enum RelaxType relax_type_;
+ double relax_max_;
+ double relax_increment_;
+ double relax_rel_tol_;
+ int max_iter_;
+ bool use_threshold_pressure_;
+ V threshold_pressures_by_interior_face_;
+
+ std::vector rq_;
+ std::vector phaseCondition_;
+ V well_perforation_pressure_diffs_; // Diff to bhp for each well perforation.
+
+ LinearisedBlackoilResidual residual_;
+
+ std::vector primalVariable_;
+
+ // Private methods.
+ SolutionState
+ constantState(const PolymerBlackoilState& x,
+ const WellStateFullyImplicitBlackoil& xw);
+
+ SolutionState
+ variableState(const PolymerBlackoilState& x,
+ const WellStateFullyImplicitBlackoil& xw);
+
+ void
+ computeAccum(const SolutionState& state,
+ const int aix );
+
+ void computeWellConnectionPressures(const SolutionState& state,
+ const WellStateFullyImplicitBlackoil& xw);
+
+ void
+ addWellControlEq(const SolutionState& state,
+ const WellStateFullyImplicitBlackoil& xw,
+ const V& aliveWells);
+
+ void
+ addWellEq(const SolutionState& state,
+ WellStateFullyImplicitBlackoil& xw,
+ V& aliveWells,
+ const std::vector& polymer_inflow);
+
+ void updateWellControls(ADB& bhp,
+ ADB& well_phase_flow_rate,
+ WellStateFullyImplicitBlackoil& xw) const;
+
+ void
+ assemble(const V& dtpv,
+ const PolymerBlackoilState& x,
+ WellStateFullyImplicitBlackoil& xw,
+ const std::vector& polymer_inflow);
+
+ V solveJacobianSystem() const;
+
+ void updateState(const V& dx,
+ PolymerBlackoilState& state,
+ WellStateFullyImplicitBlackoil& well_state);
+
+ std::vector
+ computePressures(const SolutionState& state) const;
+
+ std::vector
+ computeRelPerm(const SolutionState& state) const;
+
+ std::vector
+ computeRelPermWells(const SolutionState& state,
+ const DataBlock& well_s,
+ const std::vector& well_cells) const;
+
+ void
+ computeMassFlux(const int actph ,
+ const V& transi,
+ const ADB& kr ,
+ const ADB& p ,
+ const SolutionState& state );
+
+ void
+ computeMassFlux(const V& trans,
+ const std::vector& kr,
+ const std::vector& phasePressure,
+ const SolutionState& state);
+
+ void
+ computeCmax(PolymerBlackoilState& state,
+ const ADB& c);
+
+ ADB
+ computeMc(const SolutionState& state) const;
+
+ ADB
+ rockPorosity(const ADB& p) const;
+
+ ADB
+ rockPermeability(const ADB& p) const;
+ void applyThresholdPressures(ADB& dp);
+
+ double
+ residualNorm() const;
+
+ std::vector residuals() const;
+
+ ADB
+ fluidViscosity(const int phase,
+ const ADB& p ,
+ const ADB& rs ,
+ const ADB& rv ,
+ const std::vector& cond,
+ const std::vector& cells) const;
+
+ ADB
+ fluidReciprocFVF(const int phase,
+ const ADB& p ,
+ const ADB& rs ,
+ const ADB& rv ,
+ const std::vector& cond,
+ const std::vector& cells) const;
+
+ ADB
+ fluidDensity(const int phase,
+ const ADB& p ,
+ const ADB& rs ,
+ const ADB& rv ,
+ const std::vector& cond,
+ const std::vector& cells) const;
+
+ V
+ fluidRsSat(const V& p,
+ const V& so,
+ const std::vector& cells) const;
+
+ ADB
+ fluidRsSat(const ADB& p,
+ const ADB& so,
+ const std::vector& cells) const;
+
+ V
+ fluidRvSat(const V& p,
+ const V& so,
+ const std::vector& cells) const;
+
+ ADB
+ fluidRvSat(const ADB& p,
+ const ADB& so,
+ const std::vector& cells) const;
+
+
+ ADB
+ poroMult(const ADB& p) const;
+
+ ADB
+ transMult(const ADB& p) const;
+
+ void
+ classifyCondition(const SolutionState& state,
+ std::vector& cond ) const;
+
+ const std::vector
+ phaseCondition() const {return phaseCondition_;}
+
+ void
+ classifyCondition(const PolymerBlackoilState& state);
+
+
+ /// update the primal variable for Sg, Rv or Rs. The Gas phase must
+ /// be active to call this method.
+ void
+ updatePrimalVariableFromState(const PolymerBlackoilState& state);
+
+ /// Update the phaseCondition_ member based on the primalVariable_ member.
+ void
+ updatePhaseCondFromPrimalVariable();
+
+ /// Compute convergence based on total mass balance (tol_mb) and maximum
+ /// residual mass balance (tol_cnv).
+ bool getConvergence(const double dt);
+
+ void detectNewtonOscillations(const std::vector>& residual_history,
+ const int it, const double relaxRelTol,
+ bool& oscillate, bool& stagnate) const;
+
+ void stablizeNewton(V& dx, V& dxOld, const double omega, const RelaxType relax_type) const;
+
+ double dpMaxRel() const { return dp_max_rel_; }
+ double dsMax() const { return ds_max_; }
+ double drsMaxRel() const { return drs_max_rel_; }
+ enum RelaxType relaxType() const { return relax_type_; }
+ double relaxMax() const { return relax_max_; };
+ double relaxIncrement() const { return relax_increment_; };
+ double relaxRelTol() const { return relax_rel_tol_; };
+ double maxIter() const { return max_iter_; }
+
+ };
+} // namespace Opm
+
+#include "FullyImplicitBlackoilPolymerSolver_impl.hpp"
+
+#endif // OPM_FULLYIMPLICITBLACKOILPOLYMERSOLVER_HEADER_INCLUDED
diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp
new file mode 100644
index 000000000..100040572
--- /dev/null
+++ b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp
@@ -0,0 +1,2335 @@
+/*
+ Copyright 2013 SINTEF ICT, Applied Mathematics.
+ Copyright 2014 STATOIL ASA.
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+//#include
+
+// A debugging utility.
+#define DUMP(foo) \
+ do { \
+ std::cout << "==========================================\n" \
+ << #foo ":\n" \
+ << collapseJacs(foo) << std::endl; \
+ } while (0)
+
+#define DUMPVAL(foo) \
+ do { \
+ std::cout << "==========================================\n" \
+ << #foo ":\n" \
+ << foo.value() << std::endl; \
+ } while (0)
+
+#define DISKVAL(foo) \
+ do { \
+ std::ofstream os(#foo); \
+ os.precision(16); \
+ os << foo.value() << std::endl; \
+ } while (0)
+
+
+namespace Opm {
+
+typedef AutoDiffBlock ADB;
+typedef ADB::V V;
+typedef ADB::M M;
+typedef Eigen::Array DataBlock;
+
+
+namespace {
+
+
+ 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
+ V computePerfPress(const Grid& grid, const Wells& wells, const V& rho, const double grav)
+ {
+ using namespace Opm::AutoDiffGrid;
+ const int nw = wells.number_of_wells;
+ const int nperf = wells.well_connpos[nw];
+ const int dim = dimensions(grid);
+ V wdp = V::Zero(nperf,1);
+ assert(wdp.size() == rho.size());
+
+ // Main loop, iterate over all perforations,
+ // using the following formula:
+ // wdp(perf) = g*(perf_z - well_ref_z)*rho(perf)
+ // where the total density rho(perf) is taken to be
+ // sum_p (rho_p*saturation_p) in the perforation cell.
+ // [although this is computed on the outside of this function].
+ for (int w = 0; w < nw; ++w) {
+ const double ref_depth = wells.depth_ref[w];
+ for (int j = wells.well_connpos[w]; j < wells.well_connpos[w + 1]; ++j) {
+ const int cell = wells.well_cells[j];
+ const double cell_depth = cellCentroid(grid, cell)[dim - 1];
+ wdp[j] = rho[j]*grav*(cell_depth - ref_depth);
+ }
+ }
+ return wdp;
+ }
+
+
+
+ template
+ std::vector
+ activePhases(const PU& pu)
+ {
+ const int maxnp = Opm::BlackoilPhases::MaxNumPhases;
+ std::vector active(maxnp, false);
+
+ for (int p = 0; p < pu.MaxNumPhases; ++p) {
+ active[ p ] = pu.phase_used[ p ] != 0;
+ }
+
+ return active;
+ }
+
+
+
+ template
+ std::vector
+ active2Canonical(const PU& pu)
+ {
+ const int maxnp = Opm::BlackoilPhases::MaxNumPhases;
+ std::vector act2can(maxnp, -1);
+
+ for (int phase = 0; phase < maxnp; ++phase) {
+ if (pu.phase_used[ phase ]) {
+ act2can[ pu.phase_pos[ phase ] ] = phase;
+ }
+ }
+
+ return act2can;
+ }
+
+
+ template
+ int polymerPos(const PU& pu)
+ {
+ const int maxnp = Opm::BlackoilPhases::MaxNumPhases;
+ int pos = 0;
+ for (int phase = 0; phase < maxnp; ++phase) {
+ if (pu.phase_used[phase]) {
+ pos++;
+ }
+ }
+
+ return pos;
+ }
+} // Anonymous namespace
+
+
+
+ template
+ FullyImplicitBlackoilPolymerSolver::
+ FullyImplicitBlackoilPolymerSolver(const parameter::ParameterGroup& param,
+ const Grid& grid ,
+ const BlackoilPropsAdInterface& fluid,
+ const DerivedGeology& geo ,
+ const RockCompressibility* rock_comp_props,
+ const PolymerPropsAd& polymer_props_ad,
+ const Wells& wells,
+ const NewtonIterationBlackoilInterface& linsolver,
+ const bool has_disgas,
+ const bool has_vapoil,
+ const bool has_polymer)
+ : grid_ (grid)
+ , fluid_ (fluid)
+ , geo_ (geo)
+ , rock_comp_props_(rock_comp_props)
+ , polymer_props_ad_(polymer_props_ad)
+ , wells_ (wells)
+ , linsolver_ (linsolver)
+ , active_(activePhases(fluid.phaseUsage()))
+ , canph_ (active2Canonical(fluid.phaseUsage()))
+ , cells_ (buildAllCells(Opm::AutoDiffGrid::numCells(grid)))
+ , ops_ (grid)
+ , wops_ (wells)
+ , cmax_(V::Zero(Opm::AutoDiffGrid::numCells(grid)))
+ , has_disgas_(has_disgas)
+ , has_vapoil_(has_vapoil)
+ , has_polymer_(has_polymer)
+ , poly_pos_(polymerPos(fluid.phaseUsage()))
+ , dp_max_rel_ (1.0e9)
+ , ds_max_ (0.2)
+ , drs_max_rel_ (1.0e9)
+ , relax_type_ (DAMPEN)
+ , relax_max_ (0.5)
+ , relax_increment_ (0.1)
+ , relax_rel_tol_ (0.2)
+ , max_iter_ (15)
+ , use_threshold_pressure_(false)
+ , rq_ (fluid.numPhases())
+ , phaseCondition_(AutoDiffGrid::numCells(grid))
+ , residual_ ( { std::vector(fluid.numPhases(), ADB::null()),
+ ADB::null(),
+ ADB::null() } )
+ {
+ if (has_polymer_) {
+ if (!active_[Water]) {
+ OPM_THROW(std::logic_error, "Polymer must solved in water!\n");
+ }
+ // If deck has polymer, residual_ should contain polymer equation.
+ rq_.resize(fluid_.numPhases()+1);
+ residual_.material_balance_eq.resize(fluid_.numPhases()+1, ADB::null());
+ assert(poly_pos_ == fluid_.numPhases());
+ }
+ dp_max_rel_ = param.getDefault("dp_max_rel", dp_max_rel_);
+ ds_max_ = param.getDefault("ds_max", ds_max_);
+ drs_max_rel_ = param.getDefault("drs_max_rel", drs_max_rel_);
+ relax_max_ = param.getDefault("relax_max", relax_max_);
+ max_iter_ = param.getDefault("max_iter", max_iter_);
+
+ std::string relaxation_type = param.getDefault("relax_type", std::string("dampen"));
+ if (relaxation_type == "dampen") {
+ relax_type_ = DAMPEN;
+ } else if (relaxation_type == "sor") {
+ relax_type_ = SOR;
+ } else {
+ OPM_THROW(std::runtime_error, "Unknown Relaxtion Type " << relaxation_type);
+ }
+ }
+
+
+
+ template
+ void
+ FullyImplicitBlackoilPolymerSolver::
+ setThresholdPressures(const std::vector& threshold_pressures_by_face)
+ {
+ const int num_faces = AutoDiffGrid::numFaces(grid_);
+ if (int(threshold_pressures_by_face.size()) != num_faces) {
+ OPM_THROW(std::runtime_error, "Illegal size of threshold_pressures_by_face input, must be equal to number of faces.");
+ }
+ use_threshold_pressure_ = true;
+ // Map to interior faces.
+ const int num_ifaces = ops_.internal_faces.size();
+ threshold_pressures_by_interior_face_.resize(num_ifaces);
+ for (int ii = 0; ii < num_ifaces; ++ii) {
+ threshold_pressures_by_interior_face_[ii] = threshold_pressures_by_face[ops_.internal_faces[ii]];
+ }
+ }
+
+
+
+
+ template
+ void
+ FullyImplicitBlackoilPolymerSolver::
+ step(const double dt,
+ PolymerBlackoilState& x ,
+ WellStateFullyImplicitBlackoil& xw,
+ const std::vector& polymer_inflow)
+ {
+ const V pvdt = geo_.poreVolume() / dt;
+
+ if (active_[Gas]) { updatePrimalVariableFromState(x); }
+
+ {
+ const SolutionState state = constantState(x, xw);
+ computeCmax(x, state.concentration);
+ computeAccum(state, 0);
+ computeWellConnectionPressures(state, xw);
+ }
+
+ std::vector> residual_history;
+
+ assemble(pvdt, x, xw, polymer_inflow);
+
+
+ bool converged = false;
+ double omega = 1.;
+ const double r0 = residualNorm();
+
+ residual_history.push_back(residuals());
+
+ converged = getConvergence(dt);
+
+ int it = 0;
+ std::cout << "\nIteration Residual\n"
+ << std::setw(9) << it << std::setprecision(9)
+ << std::setw(18) << r0 << std::endl;
+
+ const int sizeNonLinear = residual_.sizeNonLinear();
+
+ V dxOld = V::Zero(sizeNonLinear);
+
+ bool isOscillate = false;
+ bool isStagnate = false;
+ const enum RelaxType relaxtype = relaxType();
+
+ while ((!converged) && (it < maxIter())) {
+ V dx = solveJacobianSystem();
+
+ detectNewtonOscillations(residual_history, it, relaxRelTol(), isOscillate, isStagnate);
+
+ if (isOscillate) {
+ omega -= relaxIncrement();
+ omega = std::max(omega, relaxMax());
+ std::cout << " Oscillating behavior detected: Relaxation set to " << omega << std::endl;
+ }
+
+ stablizeNewton(dx, dxOld, omega, relaxtype);
+
+ updateState(dx, x, xw);
+
+ assemble(pvdt, x, xw, polymer_inflow);
+
+ const double r = residualNorm();
+
+ residual_history.push_back(residuals());
+
+ converged = getConvergence(dt);
+
+ it += 1;
+ std::cout << std::setw(9) << it << std::setprecision(9)
+ << std::setw(18) << r << std::endl;
+ }
+
+ if (!converged) {
+ std::cerr << "Failed to compute converged solution in " << it << " iterations. Ignoring!\n";
+ // OPM_THROW(std::runtime_error, "Failed to compute converged solution in " << it << " iterations.");
+ }
+ }
+
+
+
+
+
+ template
+ FullyImplicitBlackoilPolymerSolver::ReservoirResidualQuant::ReservoirResidualQuant()
+ : accum(2, ADB::null())
+ , mflux( ADB::null())
+ , b ( ADB::null())
+ , head ( ADB::null())
+ , mob ( ADB::null())
+ {
+ }
+
+
+
+
+
+ template
+ FullyImplicitBlackoilPolymerSolver::SolutionState::SolutionState(const int np)
+ : pressure ( ADB::null())
+ , saturation(np, ADB::null())
+ , rs ( ADB::null())
+ , rv ( ADB::null())
+ , concentration( ADB::null())
+ , qs ( ADB::null())
+ , bhp ( ADB::null())
+ {
+ }
+
+
+
+
+
+ template
+ FullyImplicitBlackoilPolymerSolver::
+ WellOps::WellOps(const Wells& wells)
+ : w2p(wells.well_connpos[ wells.number_of_wells ],
+ wells.number_of_wells)
+ , p2w(wells.number_of_wells,
+ wells.well_connpos[ wells.number_of_wells ])
+ {
+ const int nw = wells.number_of_wells;
+ const int* const wpos = wells.well_connpos;
+
+ typedef Eigen::Triplet Tri;
+
+ std::vector scatter, gather;
+ scatter.reserve(wpos[nw]);
+ gather .reserve(wpos[nw]);
+
+ for (int w = 0, i = 0; w < nw; ++w) {
+ for (; i < wpos[ w + 1 ]; ++i) {
+ scatter.push_back(Tri(i, w, 1.0));
+ gather .push_back(Tri(w, i, 1.0));
+ }
+ }
+
+ w2p.setFromTriplets(scatter.begin(), scatter.end());
+ p2w.setFromTriplets(gather .begin(), gather .end());
+ }
+
+
+
+
+
+ template
+ typename FullyImplicitBlackoilPolymerSolver::SolutionState
+ FullyImplicitBlackoilPolymerSolver::constantState(const PolymerBlackoilState& x,
+ const WellStateFullyImplicitBlackoil& xw)
+ {
+ auto state = variableState(x, xw);
+
+ // HACK: throw away the derivatives. this may not be the most
+ // performant way to do things, but it will make the state
+ // automatically consistent with variableState() (and doing
+ // things automatically is all the rage in this module ;)
+ state.pressure = ADB::constant(state.pressure.value());
+ state.rs = ADB::constant(state.rs.value());
+ state.rv = ADB::constant(state.rv.value());
+ state.concentration = ADB::constant(state.concentration.value());
+ for (int phaseIdx= 0; phaseIdx < x.numPhases(); ++ phaseIdx)
+ state.saturation[phaseIdx] = ADB::constant(state.saturation[phaseIdx].value());
+ state.qs = ADB::constant(state.qs.value());
+ state.bhp = ADB::constant(state.bhp.value());
+
+ return state;
+ }
+
+
+
+
+
+ template
+ typename FullyImplicitBlackoilPolymerSolver::SolutionState
+ FullyImplicitBlackoilPolymerSolver::variableState(const PolymerBlackoilState& x,
+ const WellStateFullyImplicitBlackoil& xw)
+ {
+ using namespace Opm::AutoDiffGrid;
+ const int nc = numCells(grid_);
+ const int np = x.numPhases();
+
+ std::vector vars0;
+ // p, Sw and Rs, Rv or Sg, concentration are used as primary depending on solution conditions
+ vars0.reserve(np + 2);
+ // Initial pressure.
+ assert (not x.pressure().empty());
+ const V p = Eigen::Map(& x.pressure()[0], nc, 1);
+ vars0.push_back(p);
+
+ // Initial saturation.
+ assert (not x.saturation().empty());
+ const DataBlock s = Eigen::Map(& x.saturation()[0], nc, np);
+ const Opm::PhaseUsage pu = fluid_.phaseUsage();
+ // We do not handle a Water/Gas situation correctly, guard against it.
+ assert (active_[ Oil]);
+ if (active_[ Water ]) {
+ const V sw = s.col(pu.phase_pos[ Water ]);
+ vars0.push_back(sw);
+ }
+
+ // store cell status in vectors
+ V isRs = V::Zero(nc,1);
+ V isRv = V::Zero(nc,1);
+ V isSg = V::Zero(nc,1);
+
+ if (active_[ Gas ]){
+ for (int c = 0; c < nc ; c++ ) {
+ switch (primalVariable_[c]) {
+ case PrimalVariables::RS:
+ isRs[c] = 1;
+ break;
+
+ case PrimalVariables::RV:
+ isRv[c] = 1;
+ break;
+
+ default:
+ isSg[c] = 1;
+ break;
+ }
+ }
+
+
+ // define new primary variable xvar depending on solution condition
+ V xvar(nc);
+ const V sg = s.col(pu.phase_pos[ Gas ]);
+ const V rs = Eigen::Map(& x.gasoilratio()[0], x.gasoilratio().size());
+ const V rv = Eigen::Map(& x.rv()[0], x.rv().size());
+ xvar = isRs*rs + isRv*rv + isSg*sg;
+ vars0.push_back(xvar);
+ }
+
+ // Initial polymer concentration.
+ if (has_polymer_) {
+ assert (not x.concentration().empty());
+ const V c = Eigen::Map(& x.concentration()[0], nc, 1);
+ vars0.push_back(c);
+ }
+
+ // Initial well rates.
+ assert (not xw.wellRates().empty());
+ // Need to reshuffle well rates, from phase running fastest
+ // to wells running fastest.
+ const int nw = wells_.number_of_wells;
+ // The transpose() below switches the ordering.
+ const DataBlock wrates = Eigen::Map(& xw.wellRates()[0], nw, np).transpose();
+ const V qs = Eigen::Map(wrates.data(), nw*np);
+ vars0.push_back(qs);
+
+ // Initial well bottom-hole pressure.
+ assert (not xw.bhp().empty());
+ const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size());
+ vars0.push_back(bhp);
+
+ std::vector vars = ADB::variables(vars0);
+
+ SolutionState state(np);
+
+ // Pressure.
+ int nextvar = 0;
+ state.pressure = vars[ nextvar++ ];
+
+ // Saturations
+ const std::vector& bpat = vars[0].blockPattern();
+ {
+ ADB so = ADB::constant(V::Ones(nc, 1), bpat);
+
+ if (active_[ Water ]) {
+ ADB& sw = vars[ nextvar++ ];
+ state.saturation[pu.phase_pos[ Water ]] = sw;
+ so = so - sw;
+ }
+
+ if (active_[ Gas]) {
+ // Define Sg Rs and Rv in terms of xvar.
+ const ADB& xvar = vars[ nextvar++ ];
+ const ADB& sg = isSg*xvar + isRv* so;
+ state.saturation[ pu.phase_pos[ Gas ] ] = sg;
+ so = so - sg;
+ const ADB rsSat = fluidRsSat(state.pressure, so, cells_);
+ const ADB rvSat = fluidRvSat(state.pressure, so, cells_);
+
+ if (has_disgas_) {
+ state.rs = (1-isRs) * rsSat + isRs*xvar;
+ } else {
+ state.rs = rsSat;
+ }
+ if (has_vapoil_) {
+ state.rv = (1-isRv) * rvSat + isRv*xvar;
+ } else {
+ state.rv = rvSat;
+ }
+ }
+
+ if (active_[ Oil ]) {
+ // Note that so is never a primary variable.
+ state.saturation[ pu.phase_pos[ Oil ] ] = so;
+ }
+ }
+
+ // Concentration.
+ if (has_polymer_) {
+ state.concentration = vars[nextvar++];
+ }
+ // Qs.
+ state.qs = vars[ nextvar++ ];
+
+ // Bhp.
+ state.bhp = vars[ nextvar++ ];
+
+ assert(nextvar == int(vars.size()));
+
+ return state;
+ }
+
+
+
+
+
+ template
+ void
+ FullyImplicitBlackoilPolymerSolver::computeAccum(const SolutionState& state,
+ const int aix )
+ {
+ const Opm::PhaseUsage& pu = fluid_.phaseUsage();
+
+ const ADB& press = state.pressure;
+ const std::vector& sat = state.saturation;
+ const ADB& rs = state.rs;
+ const ADB& rv = state.rv;
+ const ADB& c = state.concentration;
+
+ const std::vector cond = phaseCondition();
+ std::vector pressure = computePressures(state);
+
+ const ADB pv_mult = poroMult(press);
+
+ const int maxnp = Opm::BlackoilPhases::MaxNumPhases;
+ for (int phase = 0; phase < maxnp; ++phase) {
+ if (active_[ phase ]) {
+ const int pos = pu.phase_pos[ phase ];
+ rq_[pos].b = fluidReciprocFVF(phase, pressure[phase], rs, rv, cond, cells_);
+ rq_[pos].accum[aix] = pv_mult * rq_[pos].b * sat[pos];
+ // DUMP(rq_[pos].b);
+ // DUMP(rq_[pos].accum[aix]);
+ }
+ }
+
+ if (active_[ Oil ] && active_[ Gas ]) {
+ // Account for gas dissolved in oil and vaporized oil
+ const int po = pu.phase_pos[ Oil ];
+ const int pg = pu.phase_pos[ Gas ];
+
+ rq_[pg].accum[aix] += state.rs * rq_[po].accum[aix];
+ rq_[po].accum[aix] += state.rv * rq_[pg].accum[aix];
+ //DUMP(rq_[pg].accum[aix]);
+ }
+ if (has_polymer_) {
+ // compute polymer properties.
+ const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern());
+ const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax);
+ const double rho_rock = polymer_props_ad_.rockDensity();
+ const V phi = Eigen::Map(& fluid_.porosity()[0], AutoDiffGrid::numCells(grid_), 1);
+ const double dead_pore_vol = polymer_props_ad_.deadPoreVol();
+ // compute total phases and determin polymer position.
+ rq_[poly_pos_].accum[aix] = pv_mult * rq_[pu.phase_pos[Water]].b * sat[pu.phase_pos[Water]] * c * (1. - dead_pore_vol)
+ + pv_mult * rho_rock * (1. - phi) / phi * ads;
+ }
+ }
+
+
+
+
+
+ template
+ void FullyImplicitBlackoilPolymerSolver::computeCmax(PolymerBlackoilState& state,
+ const ADB& c)
+ {
+ const int nc = AutoDiffGrid::numCells(grid_);
+ for (int i = 0; i < nc; ++i) {
+ cmax_(i) = std::max(cmax_(i), c.value()(i));
+ }
+ std::copy(&cmax_[0], &cmax_[0] + nc, state.maxconcentration().begin());
+ }
+
+
+
+
+
+ template
+ void FullyImplicitBlackoilPolymerSolver::computeWellConnectionPressures(const SolutionState& state,
+ const WellStateFullyImplicitBlackoil& xw)
+ {
+ using namespace Opm::AutoDiffGrid;
+ // 1. Compute properties required by computeConnectionPressureDelta().
+ // Note that some of the complexity of this part is due to the function
+ // taking std::vector arguments, and not Eigen objects.
+ const int nperf = wells_.well_connpos[wells_.number_of_wells];
+ const std::vector well_cells(wells_.well_cells, wells_.well_cells + nperf);
+ // Compute b, rsmax, rvmax values for perforations.
+ const ADB perf_press = subset(state.pressure, well_cells);
+ std::vector perf_cond(nperf);
+ const std::vector& pc = phaseCondition();
+ for (int perf = 0; perf < nperf; ++perf) {
+ perf_cond[perf] = pc[well_cells[perf]];
+ }
+ const PhaseUsage& pu = fluid_.phaseUsage();
+ DataBlock b(nperf, pu.num_phases);
+ std::vector rssat_perf(nperf, 0.0);
+ std::vector rvsat_perf(nperf, 0.0);
+ if (pu.phase_used[BlackoilPhases::Aqua]) {
+ const ADB bw = fluid_.bWat(perf_press, well_cells);
+ b.col(pu.phase_pos[BlackoilPhases::Aqua]) = bw.value();
+ }
+ assert(active_[Oil]);
+ const ADB perf_so = subset(state.saturation[pu.phase_pos[Oil]], well_cells);
+ if (pu.phase_used[BlackoilPhases::Liquid]) {
+ const ADB perf_rs = subset(state.rs, well_cells);
+ const ADB bo = fluid_.bOil(perf_press, perf_rs, perf_cond, well_cells);
+ b.col(pu.phase_pos[BlackoilPhases::Liquid]) = bo.value();
+ const V rssat = fluidRsSat(perf_press.value(), perf_so.value(), well_cells);
+ rssat_perf.assign(rssat.data(), rssat.data() + nperf);
+ }
+ if (pu.phase_used[BlackoilPhases::Vapour]) {
+ const ADB perf_rv = subset(state.rv, well_cells);
+ const ADB bg = fluid_.bGas(perf_press, perf_rv, perf_cond, well_cells);
+ b.col(pu.phase_pos[BlackoilPhases::Vapour]) = bg.value();
+ const V rvsat = fluidRvSat(perf_press.value(), perf_so.value(), well_cells);
+ rvsat_perf.assign(rvsat.data(), rvsat.data() + nperf);
+ }
+ // b is row major, so can just copy data.
+ std::vector b_perf(b.data(), b.data() + nperf * pu.num_phases);
+ // Extract well connection depths.
+ const V depth = cellCentroidsZToEigen(grid_);
+ const V pdepth = subset(depth, well_cells);
+ std::vector perf_depth(pdepth.data(), pdepth.data() + nperf);
+ // Surface density.
+ std::vector surf_dens(fluid_.surfaceDensity(), fluid_.surfaceDensity() + pu.num_phases);
+ // Gravity
+ double grav = 0.0;
+ const double* g = geo_.gravity();
+ const int dim = dimensions(grid_);
+ if (g) {
+ // Guard against gravity in anything but last dimension.
+ for (int dd = 0; dd < dim - 1; ++dd) {
+ assert(g[dd] == 0.0);
+ }
+ grav = g[dim - 1];
+ }
+
+ // 2. Compute pressure deltas, and store the results.
+ std::vector cdp = WellDensitySegmented
+ ::computeConnectionPressureDelta(wells_, xw, fluid_.phaseUsage(),
+ b_perf, rssat_perf, rvsat_perf, perf_depth,
+ surf_dens, grav);
+ well_perforation_pressure_diffs_ = Eigen::Map(cdp.data(), nperf);
+ }
+
+
+
+
+
+ template
+ void
+ FullyImplicitBlackoilPolymerSolver::
+ assemble(const V& pvdt,
+ const PolymerBlackoilState& x ,
+ WellStateFullyImplicitBlackoil& xw,
+ const std::vector& polymer_inflow)
+ {
+ using namespace Opm::AutoDiffGrid;
+ // Create the primary variables.
+ SolutionState state = variableState(x, xw);
+
+ // DISKVAL(state.pressure);
+ // DISKVAL(state.saturation[0]);
+ // DISKVAL(state.saturation[1]);
+ // DISKVAL(state.saturation[2]);
+ // DISKVAL(state.rs);
+ // DISKVAL(state.rv);
+ // DISKVAL(state.qs);
+ // DISKVAL(state.bhp);
+
+ // -------- Mass balance equations --------
+
+ // Compute b_p and the accumulation term b_p*s_p for each phase,
+ // except gas. For gas, we compute b_g*s_g + Rs*b_o*s_o.
+ // These quantities are stored in rq_[phase].accum[1].
+ // The corresponding accumulation terms from the start of
+ // the timestep (b^0_p*s^0_p etc.) were already computed
+ // in step() and stored in rq_[phase].accum[0].
+ computeAccum(state, 1);
+
+ // Set up the common parts of the mass balance equations
+ // for each active phase.
+ const V transi = subset(geo_.transmissibility(), ops_.internal_faces);
+ const std::vector kr = computeRelPerm(state);
+ const std::vector pressures = computePressures(state);
+ computeMassFlux(transi, kr, pressures, state);
+ for (int phaseIdx = 0; phaseIdx < fluid_.numPhases(); ++phaseIdx) {
+ // std::cout << "===== kr[" << phase << "] = \n" << std::endl;
+ // std::cout << kr[phase];
+ // std::cout << "===== rq_[" << phase << "].mflux = \n" << std::endl;
+ // std::cout << rq_[phase].mflux;
+ residual_.material_balance_eq[ phaseIdx ] =
+ pvdt*(rq_[phaseIdx].accum[1] - rq_[phaseIdx].accum[0])
+ + ops_.div*rq_[phaseIdx].mflux;
+
+
+ // DUMP(ops_.div*rq_[phase].mflux);
+ // DUMP(residual_.material_balance_eq[phase]);
+ }
+
+ // -------- Extra (optional) rs and rv contributions to the mass balance equations --------
+
+ // Add the extra (flux) terms to the mass balance equations
+ // From gas dissolved in the oil phase (rs) and oil vaporized in the gas phase (rv)
+ // The extra terms in the accumulation part of the equation are already handled.
+ if (active_[ Oil ] && active_[ Gas ]) {
+ const int po = fluid_.phaseUsage().phase_pos[ Oil ];
+ const int pg = fluid_.phaseUsage().phase_pos[ Gas ];
+
+ const UpwindSelector upwindOil(grid_, ops_,
+ rq_[po].head.value());
+ const ADB rs_face = upwindOil.select(state.rs);
+
+ const UpwindSelector upwindGas(grid_, ops_,
+ rq_[pg].head.value());
+ const ADB rv_face = upwindGas.select(state.rv);
+
+ residual_.material_balance_eq[ pg ] += ops_.div * (rs_face * rq_[po].mflux);
+ residual_.material_balance_eq[ po ] += ops_.div * (rv_face * rq_[pg].mflux);
+
+ // DUMP(residual_.material_balance_eq[ Gas ]);
+
+ }
+
+ // Add polymer equation.
+ if (has_polymer_) {
+ residual_.material_balance_eq[poly_pos_] = pvdt*(rq_[poly_pos_].accum[1] - rq_[poly_pos_].accum[0])
+ + ops_.div*rq_[poly_pos_].mflux;
+ }
+
+ // Note: updateWellControls() can change all its arguments if
+ // a well control is switched.
+ updateWellControls(state.bhp, state.qs, xw);
+ V aliveWells;
+ addWellEq(state, xw, aliveWells, polymer_inflow);
+ addWellControlEq(state, xw, aliveWells);
+ }
+
+
+
+
+
+ template
+ void FullyImplicitBlackoilPolymerSolver::addWellEq(const SolutionState& state,
+ WellStateFullyImplicitBlackoil& xw,
+ V& aliveWells,
+ const std::vector