mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Merge pull request #552 from akva2/import_polymer2
Import opm-polymer into opm-autodiff
This commit is contained in:
commit
eb3cff97da
@ -47,6 +47,17 @@ list (APPEND MAIN_SOURCE_FILES
|
||||
opm/autodiff/VFPProdProperties.cpp
|
||||
opm/autodiff/VFPInjProperties.cpp
|
||||
opm/autodiff/WellMultiSegment.cpp
|
||||
opm/polymer/CompressibleTpfaPolymer.cpp
|
||||
opm/polymer/IncompTpfaPolymer.cpp
|
||||
opm/polymer/PolymerInflow.cpp
|
||||
opm/polymer/PolymerProperties.cpp
|
||||
opm/polymer/polymerUtilities.cpp
|
||||
opm/polymer/SimulatorCompressiblePolymer.cpp
|
||||
opm/polymer/SimulatorPolymer.cpp
|
||||
opm/polymer/TransportSolverTwophaseCompressiblePolymer.cpp
|
||||
opm/polymer/TransportSolverTwophasePolymer.cpp
|
||||
opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp
|
||||
opm/polymer/fullyimplicit/PolymerPropsAd.cpp
|
||||
)
|
||||
|
||||
|
||||
@ -64,6 +75,7 @@ list (APPEND TEST_SOURCE_FILES
|
||||
tests/test_transmissibilitymultipliers.cpp
|
||||
tests/test_welldensitysegmented.cpp
|
||||
tests/test_vfpproperties.cpp
|
||||
tests/test_singlecellsolves.cpp
|
||||
)
|
||||
|
||||
list (APPEND TEST_DATA_FILES
|
||||
@ -83,6 +95,10 @@ list (APPEND EXAMPLE_SOURCE_FILES
|
||||
examples/sim_2p_incomp_ad.cpp
|
||||
examples/sim_simple.cpp
|
||||
examples/opm_init_check.cpp
|
||||
examples/sim_poly2p_comp_reorder.cpp
|
||||
examples/sim_poly2p_incomp_reorder.cpp
|
||||
examples/sim_poly_fi2p_comp_ad.cpp
|
||||
examples/flow_polymer.cpp
|
||||
)
|
||||
|
||||
# programs listed here will not only be compiled, but also marked for
|
||||
@ -92,6 +108,10 @@ list (APPEND PROGRAM_SOURCE_FILES
|
||||
examples/flow.cpp
|
||||
examples/flow_solvent.cpp
|
||||
examples/opm_init_check.cpp
|
||||
examples/sim_poly2p_comp_reorder.cpp
|
||||
examples/sim_poly2p_incomp_reorder.cpp
|
||||
examples/sim_poly_fi2p_comp_ad.cpp
|
||||
examples/flow_polymer.cpp
|
||||
)
|
||||
|
||||
# originally generated with the command:
|
||||
@ -157,5 +177,30 @@ list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/autodiff/VFPInjProperties.hpp
|
||||
opm/autodiff/WellStateMultiSegment.hpp
|
||||
opm/autodiff/WellMultiSegment.hpp
|
||||
opm/polymer/CompressibleTpfaPolymer.hpp
|
||||
opm/polymer/GravityColumnSolverPolymer.hpp
|
||||
opm/polymer/GravityColumnSolverPolymer_impl.hpp
|
||||
opm/polymer/IncompPropertiesDefaultPolymer.hpp
|
||||
opm/polymer/IncompTpfaPolymer.hpp
|
||||
opm/polymer/PolymerBlackoilState.hpp
|
||||
opm/polymer/PolymerInflow.hpp
|
||||
opm/polymer/PolymerProperties.hpp
|
||||
opm/polymer/PolymerState.hpp
|
||||
opm/polymer/polymerUtilities.hpp
|
||||
opm/polymer/SimulatorCompressiblePolymer.hpp
|
||||
opm/polymer/SimulatorPolymer.hpp
|
||||
opm/polymer/SinglePointUpwindTwoPhasePolymer.hpp
|
||||
opm/polymer/TransportSolverTwophaseCompressiblePolymer.hpp
|
||||
opm/polymer/Point2D.hpp
|
||||
opm/polymer/TransportSolverTwophasePolymer.hpp
|
||||
opm/polymer/fullyimplicit/PolymerPropsAd.hpp
|
||||
opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp
|
||||
opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp
|
||||
opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer_impl.hpp
|
||||
opm/polymer/fullyimplicit/BlackoilPolymerModel.hpp
|
||||
opm/polymer/fullyimplicit/BlackoilPolymerModel_impl.hpp
|
||||
opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp
|
||||
opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer_impl.hpp
|
||||
opm/polymer/fullyimplicit/WellStateFullyImplicitBlackoilPolymer.hpp
|
||||
)
|
||||
|
||||
|
338
examples/flow_polymer.cpp
Normal file
338
examples/flow_polymer.cpp
Normal file
@ -0,0 +1,338 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <opm/core/pressure/FlowBCManager.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/simulator/initState.hpp>
|
||||
#include <opm/core/simulator/initStateEquil.hpp>
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <opm/core/utility/thresholdPressures.hpp>
|
||||
|
||||
#include <opm/material/fluidmatrixinteractions/EclMaterialLawManager.hpp>
|
||||
#include <opm/core/props/BlackoilPropertiesBasic.hpp>
|
||||
#include <opm/core/props/BlackoilPropertiesFromDeck.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/core/linalg/LinearSolverFactory.hpp>
|
||||
#include <opm/autodiff/NewtonIterationBlackoilSimple.hpp>
|
||||
#include <opm/autodiff/NewtonIterationBlackoilCPR.hpp>
|
||||
|
||||
#include <opm/polymer/PolymerBlackoilState.hpp>
|
||||
#include <opm/autodiff/WellStateFullyImplicitBlackoil.hpp>
|
||||
#include <opm/autodiff/moduleVersion.hpp>
|
||||
|
||||
#include <opm/polymer/fullyimplicit/SimulatorFullyImplicitBlackoilPolymer.hpp>
|
||||
#include <opm/polymer/fullyimplicit/PolymerPropsAd.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
#include <opm/polymer/PolymerInflow.hpp>
|
||||
#include <opm/autodiff/BlackoilPropsAdFromDeck.hpp>
|
||||
#include <opm/autodiff/BlackoilPropsAdInterface.hpp>
|
||||
#include <opm/autodiff/GridHelpers.hpp>
|
||||
#include <opm/autodiff/createGlobalCellArray.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/OpmLog/OpmLog.hpp>
|
||||
#include <opm/parser/eclipse/OpmLog/StreamLog.hpp>
|
||||
#include <opm/parser/eclipse/OpmLog/CounterLog.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseMode.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/checkDeck.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
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::string version = moduleVersionName();
|
||||
std::cout << "**********************************************************************\n";
|
||||
std::cout << "* *\n";
|
||||
std::cout << "* This is Flow-Polymer (version " << version << ")"
|
||||
<< std::string(18 - version.size(), ' ') << "*\n";
|
||||
std::cout << "* *\n";
|
||||
std::cout << "* Flow-Polymer is a simulator for fully implicit three-phase, *\n";
|
||||
std::cout << "* four-component (black-oil + polymer) flow, and is part of OPM. *\n";
|
||||
std::cout << "* For more information see http://opm-project.org *\n";
|
||||
std::cout << "* *\n";
|
||||
std::cout << "**********************************************************************\n\n";
|
||||
}
|
||||
|
||||
// Read parameters, see if a deck was specified on the command line.
|
||||
std::cout << "--------------- Reading parameters ---------------" << std::endl;
|
||||
parameter::ParameterGroup param(argc, argv, false);
|
||||
if (!param.unhandledArguments().empty()) {
|
||||
if (param.unhandledArguments().size() != 1) {
|
||||
OPM_THROW(std::runtime_error, "You can only specify a single input deck on the command line.");
|
||||
} 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=<path to your deck>, or\n"
|
||||
" c) as a parameter in a parameter file (.param or .xml) passed to the program.\n";
|
||||
OPM_THROW(std::runtime_error, "Input deck required.");
|
||||
}
|
||||
|
||||
std::shared_ptr<GridManager> grid;
|
||||
std::shared_ptr<BlackoilPropertiesFromDeck> props;
|
||||
std::shared_ptr<BlackoilPropsAdFromDeck> new_props;
|
||||
std::shared_ptr<RockCompressibility> 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<std::string>("deck_filename");
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
std::string logFile = output_dir + "/LOGFILE.txt";
|
||||
Opm::ParserPtr parser(new Opm::Parser());
|
||||
{
|
||||
std::shared_ptr<Opm::StreamLog> streamLog = std::make_shared<Opm::StreamLog>(logFile , Opm::Log::DefaultMessageTypes);
|
||||
std::shared_ptr<Opm::CounterLog> counterLog = std::make_shared<Opm::CounterLog>(Opm::Log::DefaultMessageTypes);
|
||||
|
||||
Opm::OpmLog::addBackend( "STREAM" , streamLog );
|
||||
Opm::OpmLog::addBackend( "COUNTER" , counterLog );
|
||||
}
|
||||
|
||||
|
||||
Opm::DeckConstPtr deck;
|
||||
std::shared_ptr<EclipseState> eclipseState;
|
||||
Opm::ParseMode parseMode({{ ParseMode::PARSE_RANDOM_SLASH , InputError::IGNORE }});
|
||||
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;
|
||||
}
|
||||
|
||||
// Grid init
|
||||
std::vector<double> porv = eclipseState->getDoubleGridProperty("PORV")->getData();
|
||||
grid.reset(new GridManager(eclipseState->getEclipseGrid(), porv));
|
||||
auto &cGrid = *grid->c_grid();
|
||||
const PhaseUsage pu = Opm::phaseUsageFromDeck(deck);
|
||||
|
||||
// Rock and fluid init
|
||||
|
||||
std::vector<int> compressedToCartesianIdx;
|
||||
Opm::createGlobalCellArray(*grid->c_grid(), compressedToCartesianIdx);
|
||||
|
||||
typedef BlackoilPropsAdFromDeck::MaterialLawManager MaterialLawManager;
|
||||
auto materialLawManager = std::make_shared<MaterialLawManager>();
|
||||
materialLawManager->initFromDeck(deck, eclipseState, compressedToCartesianIdx);
|
||||
|
||||
props.reset(new BlackoilPropertiesFromDeck( deck, eclipseState, materialLawManager,
|
||||
Opm::UgGridHelpers::numCells(cGrid),
|
||||
Opm::UgGridHelpers::globalCell(cGrid),
|
||||
Opm::UgGridHelpers::cartDims(cGrid),
|
||||
param));
|
||||
new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, materialLawManager, cGrid));
|
||||
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);
|
||||
state.faceflux().resize(grid->c_grid()->number_of_faces, 0.0);
|
||||
} else {
|
||||
initBlackoilStateFromDeck(*grid->c_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 nc = grid->c_grid()->number_of_cells;
|
||||
std::vector<int> cells(nc);
|
||||
for (int c = 0; c < nc; ++c) { cells[c] = c; }
|
||||
std::vector<double> pc = state.saturation();
|
||||
props->capPress(nc, 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;
|
||||
|
||||
// Solver for Newton iterations.
|
||||
std::unique_ptr<NewtonIterationBlackoilInterface> fis_solver;
|
||||
if (param.getDefault("use_cpr", true)) {
|
||||
fis_solver.reset(new NewtonIterationBlackoilCPR(param));
|
||||
} else {
|
||||
fis_solver.reset(new NewtonIterationBlackoilSimple(param));
|
||||
}
|
||||
|
||||
Opm::ScheduleConstPtr schedule = eclipseState->getSchedule();
|
||||
Opm::TimeMapConstPtr timeMap(schedule->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.");
|
||||
}
|
||||
}
|
||||
|
||||
bool use_local_perm = param.getDefault("use_local_perm", true);
|
||||
Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, use_local_perm, grav);
|
||||
|
||||
std::map<std::pair<int, int>, double> maxDp;
|
||||
computeMaxDp(maxDp, deck, eclipseState, *grid->c_grid(), state, *props, gravity[2]);
|
||||
std::vector<double> threshold_pressures = thresholdPressures(deck, eclipseState, *grid->c_grid(), maxDp);
|
||||
|
||||
Opm::BlackoilOutputWriter
|
||||
outputWriter(cGrid, param, eclipseState, pu,
|
||||
new_props->permeability());
|
||||
|
||||
SimulatorFullyImplicitBlackoilPolymer<UnstructuredGrid>
|
||||
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,
|
||||
deck->hasKeyword("PLYSHLOG"),
|
||||
deck->hasKeyword("SHRATE"),
|
||||
eclipseState,
|
||||
outputWriter,
|
||||
deck,
|
||||
threshold_pressures);
|
||||
|
||||
if (!schedule->initOnly()){
|
||||
std::cout << "\n\n================ Starting main simulation loop ===============\n"
|
||||
<< std::flush;
|
||||
|
||||
SimulatorReport fullReport = simulator.run(eclipseState, 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);
|
||||
}
|
||||
} else {
|
||||
outputWriter.writeInit( simtimer );
|
||||
std::cout << "\n\n================ Simulation turned off ===============\n" << std::flush;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
std::cerr << "Program threw an exception: " << e.what() << "\n";
|
||||
throw;
|
||||
}
|
||||
|
318
examples/sim_poly2p_comp_reorder.cpp
Normal file
318
examples/sim_poly2p_comp_reorder.cpp
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/core/pressure/FlowBCManager.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/simulator/initState.hpp>
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <opm/core/props/BlackoilPropertiesBasic.hpp>
|
||||
#include <opm/core/props/BlackoilPropertiesFromDeck.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/core/linalg/LinearSolverFactory.hpp>
|
||||
|
||||
#include <opm/polymer/PolymerBlackoilState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/polymer/SimulatorCompressiblePolymer.hpp>
|
||||
#include <opm/polymer/PolymerInflow.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseMode.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
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 weakly compressible two-phase flow with polymer ===============\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");
|
||||
boost::scoped_ptr<GridManager> grid;
|
||||
boost::scoped_ptr<BlackoilPropertiesInterface> props;
|
||||
boost::scoped_ptr<RockCompressibility> rock_comp;
|
||||
Opm::DeckConstPtr deck;
|
||||
EclipseStateConstPtr eclipseState;
|
||||
PolymerBlackoilState state;
|
||||
Opm::PolymerProperties poly_props;
|
||||
// bool check_well_controls = false;
|
||||
// int max_well_control_iterations = 0;
|
||||
double gravity[3] = { 0.0 };
|
||||
if (use_deck) {
|
||||
std::string deck_filename = param.get<std::string>("deck_filename");
|
||||
ParserPtr parser(new Opm::Parser());
|
||||
Opm::ParseMode parseMode({{ ParseMode::PARSE_RANDOM_SLASH , InputError::IGNORE }});
|
||||
deck = parser->parseFile(deck_filename , parseMode);
|
||||
eclipseState.reset(new Opm::EclipseState(deck , parseMode));
|
||||
|
||||
// Grid init
|
||||
grid.reset(new GridManager(deck));
|
||||
// Rock and fluid init
|
||||
props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid()));
|
||||
// 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);
|
||||
} else {
|
||||
initStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state);
|
||||
}
|
||||
initBlackoilSurfvol(*grid->c_grid(), *props, state);
|
||||
// Init polymer properties.
|
||||
poly_props.readFromDeck(deck, eclipseState);
|
||||
} else {
|
||||
// Grid init.
|
||||
const int nx = param.getDefault("nx", 100);
|
||||
const int ny = param.getDefault("ny", 100);
|
||||
const int nz = param.getDefault("nz", 1);
|
||||
const double dx = param.getDefault("dx", 1.0);
|
||||
const double dy = param.getDefault("dy", 1.0);
|
||||
const double dz = param.getDefault("dz", 1.0);
|
||||
grid.reset(new GridManager(nx, ny, nz, dx, dy, dz));
|
||||
// Rock and fluid init.
|
||||
props.reset(new BlackoilPropertiesBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells));
|
||||
// Rock compressibility.
|
||||
rock_comp.reset(new RockCompressibility(param));
|
||||
// Gravity.
|
||||
gravity[2] = param.getDefault("gravity", 0.0);
|
||||
// Init state variables (saturation and pressure).
|
||||
initStateBasic(*grid->c_grid(), *props, param, gravity[2], state);
|
||||
initBlackoilSurfvol(*grid->c_grid(), *props, state);
|
||||
// Init Polymer state
|
||||
if (param.has("poly_init")) {
|
||||
double poly_init = param.getDefault("poly_init", 0.0);
|
||||
for (int cell = 0; cell < grid->c_grid()->number_of_cells; ++cell) {
|
||||
double smin[2], smax[2];
|
||||
props->satRange(1, &cell, smin, smax);
|
||||
if (state.saturation()[2*cell] > 0.5*(smin[0] + smax[0])) {
|
||||
state.concentration()[cell] = poly_init;
|
||||
state.maxconcentration()[cell] = poly_init;
|
||||
} else {
|
||||
state.saturation()[2*cell + 0] = 0.;
|
||||
state.saturation()[2*cell + 1] = 1.;
|
||||
state.concentration()[cell] = 0.;
|
||||
state.maxconcentration()[cell] = 0.;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Init polymer properties.
|
||||
// Setting defaults to provide a simple example case.
|
||||
double c_max = param.getDefault("c_max_limit", 5.0);
|
||||
double mix_param = param.getDefault("mix_param", 1.0);
|
||||
double rock_density = param.getDefault("rock_density", 1000.0);
|
||||
double dead_pore_vol = param.getDefault("dead_pore_vol", 0.15);
|
||||
double res_factor = param.getDefault("res_factor", 1.) ; // res_factor = 1 gives no change in permeability
|
||||
double c_max_ads = param.getDefault("c_max_ads", 1.);
|
||||
int ads_index = param.getDefault<int>("ads_index", Opm::PolymerProperties::NoDesorption);
|
||||
std::vector<double> c_vals_visc(2, -1e100);
|
||||
c_vals_visc[0] = 0.0;
|
||||
c_vals_visc[1] = 7.0;
|
||||
std::vector<double> visc_mult_vals(2, -1e100);
|
||||
visc_mult_vals[0] = 1.0;
|
||||
// poly_props.visc_mult_vals[1] = param.getDefault("c_max_viscmult", 30.0);
|
||||
visc_mult_vals[1] = 20.0;
|
||||
std::vector<double> c_vals_ads(3, -1e100);
|
||||
c_vals_ads[0] = 0.0;
|
||||
c_vals_ads[1] = 2.0;
|
||||
c_vals_ads[2] = 8.0;
|
||||
std::vector<double> ads_vals(3, -1e100);
|
||||
ads_vals[0] = 0.0;
|
||||
ads_vals[1] = 0.0015;
|
||||
ads_vals[2] = 0.0025;
|
||||
// ads_vals[1] = 0.0;
|
||||
// ads_vals[2] = 0.0;
|
||||
std::vector<double> water_vel_vals(2, -1e100);
|
||||
water_vel_vals[0] = 0.0;
|
||||
water_vel_vals[1] = 10.0;
|
||||
std::vector<double> shear_vrf_vals(2, -1e100);
|
||||
shear_vrf_vals[0] = 1.0;
|
||||
shear_vrf_vals[1] = 1.0;
|
||||
poly_props.set(c_max, mix_param, rock_density, dead_pore_vol, res_factor, c_max_ads,
|
||||
static_cast<Opm::PolymerProperties::AdsorptionBehaviour>(ads_index),
|
||||
c_vals_visc, visc_mult_vals, c_vals_ads, ads_vals, water_vel_vals, shear_vrf_vals);
|
||||
}
|
||||
|
||||
bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0);
|
||||
const double *grav = use_gravity ? &gravity[0] : 0;
|
||||
|
||||
// Linear solver.
|
||||
LinearSolverFactory linsolver(param);
|
||||
|
||||
// Write parameters used for later reference.
|
||||
bool output = param.getDefault("output", true);
|
||||
if (output) {
|
||||
std::string 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");
|
||||
}
|
||||
|
||||
|
||||
std::cout << "\n\n================ Starting main simulation loop ===============\n"
|
||||
<< std::flush;
|
||||
|
||||
SimulatorReport rep;
|
||||
if (!use_deck) {
|
||||
// Simple simulation without a deck.
|
||||
PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day,
|
||||
param.getDefault("poly_end_days", 800.0)*Opm::unit::day,
|
||||
param.getDefault("poly_amount", poly_props.cMax()));
|
||||
WellsManager wells;
|
||||
SimulatorCompressiblePolymer simulator(param,
|
||||
*grid->c_grid(),
|
||||
*props,
|
||||
poly_props,
|
||||
rock_comp->isActive() ? rock_comp.get() : 0,
|
||||
wells,
|
||||
polymer_inflow,
|
||||
linsolver,
|
||||
grav);
|
||||
SimulatorTimer simtimer;
|
||||
simtimer.init(param);
|
||||
warnIfUnusedParams(param);
|
||||
WellState well_state;
|
||||
well_state.init(0, state);
|
||||
rep = simulator.run(simtimer, state, well_state);
|
||||
} else {
|
||||
// With a deck, we may have more epochs etc.
|
||||
WellState well_state;
|
||||
int step = 0;
|
||||
Opm::TimeMapPtr timeMap(new Opm::TimeMap(deck));
|
||||
SimulatorTimer simtimer;
|
||||
simtimer.init(timeMap);
|
||||
// Check for WPOLYMER presence in last report step 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.");
|
||||
}
|
||||
}
|
||||
for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) {
|
||||
simtimer.setCurrentStepNum(reportStepIdx);
|
||||
|
||||
// Report on start of report step.
|
||||
std::cout << "\n\n-------------- Starting report step " << reportStepIdx << " --------------"
|
||||
<< "\n (number of remaining steps: "
|
||||
<< simtimer.numSteps() - step << ")\n\n" << std::flush;
|
||||
|
||||
// Create new wells, polymer inflow controls.
|
||||
WellsManager wells(eclipseState , reportStepIdx , *grid->c_grid(), props->permeability());
|
||||
boost::scoped_ptr<PolymerInflowInterface> polymer_inflow;
|
||||
if (use_wpolymer) {
|
||||
if (wells.c_wells() == 0) {
|
||||
OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells.");
|
||||
}
|
||||
polymer_inflow.reset(new PolymerInflowFromDeck(deck, eclipseState, *wells.c_wells(), props->numCells(), simtimer.currentStepNum()));
|
||||
} else {
|
||||
polymer_inflow.reset(new PolymerInflowBasic(param.getDefault("poly_start_days", 300.0)*Opm::unit::day,
|
||||
param.getDefault("poly_end_days", 800.0)*Opm::unit::day,
|
||||
param.getDefault("poly_amount", poly_props.cMax())));
|
||||
}
|
||||
|
||||
// @@@ HACK: we should really make a new well state and
|
||||
// properly transfer old well state to it every report step,
|
||||
// since number of wells may change etc.
|
||||
if (reportStepIdx == 0) {
|
||||
well_state.init(wells.c_wells(), state);
|
||||
}
|
||||
|
||||
// Create and run simulator.
|
||||
SimulatorCompressiblePolymer simulator(param,
|
||||
*grid->c_grid(),
|
||||
*props,
|
||||
poly_props,
|
||||
rock_comp->isActive() ? rock_comp.get() : 0,
|
||||
wells,
|
||||
*polymer_inflow,
|
||||
linsolver,
|
||||
grav);
|
||||
if (reportStepIdx == 0) {
|
||||
warnIfUnusedParams(param);
|
||||
}
|
||||
SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state);
|
||||
|
||||
// Update total timing report and remember step number.
|
||||
rep += epoch_rep;
|
||||
step = simtimer.currentStepNum();
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\n\n================ End of simulation ===============\n\n";
|
||||
rep.report(std::cout);
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
std::cerr << "Program threw an exception: " << e.what() << "\n";
|
||||
throw;
|
||||
}
|
||||
|
356
examples/sim_poly2p_incomp_reorder.cpp
Normal file
356
examples/sim_poly2p_incomp_reorder.cpp
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/core/pressure/FlowBCManager.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/simulator/initState.hpp>
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <opm/core/props/IncompPropertiesBasic.hpp>
|
||||
#include <opm/core/props/IncompPropertiesFromDeck.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/core/linalg/LinearSolverFactory.hpp>
|
||||
|
||||
#include <opm/polymer/PolymerState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/polymer/SimulatorPolymer.hpp>
|
||||
#include <opm/polymer/PolymerInflow.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseMode.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
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 incompressible two-phase flow with polymer ===============\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");
|
||||
Opm::DeckConstPtr deck;
|
||||
boost::scoped_ptr<GridManager> grid;
|
||||
boost::scoped_ptr<IncompPropertiesInterface> props;
|
||||
boost::scoped_ptr<RockCompressibility> rock_comp;
|
||||
EclipseStateConstPtr eclipseState;
|
||||
PolymerState state;
|
||||
Opm::PolymerProperties poly_props;
|
||||
// bool check_well_controls = false;
|
||||
// int max_well_control_iterations = 0;
|
||||
double gravity[3] = { 0.0 };
|
||||
if (use_deck) {
|
||||
std::string deck_filename = param.get<std::string>("deck_filename");
|
||||
Opm::ParseMode parseMode({{ ParseMode::PARSE_RANDOM_SLASH , InputError::IGNORE }});
|
||||
ParserPtr parser(new Opm::Parser());
|
||||
deck = parser->parseFile(deck_filename , parseMode);
|
||||
eclipseState.reset(new Opm::EclipseState(deck , parseMode));
|
||||
|
||||
// Grid init
|
||||
grid.reset(new GridManager(deck));
|
||||
// Rock and fluid init
|
||||
props.reset(new IncompPropertiesFromDeck(deck, eclipseState, *grid->c_grid()));
|
||||
// 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);
|
||||
} else {
|
||||
initStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state);
|
||||
}
|
||||
// Init polymer properties.
|
||||
poly_props.readFromDeck(deck, eclipseState);
|
||||
} else {
|
||||
// Grid init.
|
||||
const int nx = param.getDefault("nx", 100);
|
||||
const int ny = param.getDefault("ny", 100);
|
||||
const int nz = param.getDefault("nz", 1);
|
||||
const double dx = param.getDefault("dx", 1.0);
|
||||
const double dy = param.getDefault("dy", 1.0);
|
||||
const double dz = param.getDefault("dz", 1.0);
|
||||
grid.reset(new GridManager(nx, ny, nz, dx, dy, dz));
|
||||
// Rock and fluid init.
|
||||
props.reset(new IncompPropertiesBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells));
|
||||
// Rock compressibility.
|
||||
rock_comp.reset(new RockCompressibility(param));
|
||||
// Gravity.
|
||||
gravity[2] = param.getDefault("gravity", 0.0);
|
||||
// Init state variables (saturation and pressure).
|
||||
initStateBasic(*grid->c_grid(), *props, param, gravity[2], state);
|
||||
// Init Polymer state
|
||||
if (param.has("poly_init")) {
|
||||
double poly_init = param.getDefault("poly_init", 0.0);
|
||||
for (int cell = 0; cell < grid->c_grid()->number_of_cells; ++cell) {
|
||||
double smin[2], smax[2];
|
||||
props->satRange(1, &cell, smin, smax);
|
||||
if (state.saturation()[2*cell] > 0.5*(smin[0] + smax[0])) {
|
||||
state.concentration()[cell] = poly_init;
|
||||
state.maxconcentration()[cell] = poly_init;
|
||||
} else {
|
||||
state.saturation()[2*cell + 0] = 0.;
|
||||
state.saturation()[2*cell + 1] = 1.;
|
||||
state.concentration()[cell] = 0.;
|
||||
state.maxconcentration()[cell] = 0.;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Init polymer properties.
|
||||
// Setting defaults to provide a simple example case.
|
||||
double c_max = param.getDefault("c_max_limit", 5.0);
|
||||
double mix_param = param.getDefault("mix_param", 1.0);
|
||||
double rock_density = param.getDefault("rock_density", 1000.0);
|
||||
double dead_pore_vol = param.getDefault("dead_pore_vol", 0.15);
|
||||
double res_factor = param.getDefault("res_factor", 1.) ; // res_factor = 1 gives no change in permeability
|
||||
double c_max_ads = param.getDefault("c_max_ads", 1.);
|
||||
int ads_index = param.getDefault<int>("ads_index", Opm::PolymerProperties::NoDesorption);
|
||||
std::vector<double> c_vals_visc(2, -1e100);
|
||||
c_vals_visc[0] = 0.0;
|
||||
c_vals_visc[1] = 7.0;
|
||||
std::vector<double> visc_mult_vals(2, -1e100);
|
||||
visc_mult_vals[0] = 1.0;
|
||||
// poly_props.visc_mult_vals[1] = param.getDefault("c_max_viscmult", 30.0);
|
||||
visc_mult_vals[1] = 20.0;
|
||||
std::vector<double> c_vals_ads(3, -1e100);
|
||||
c_vals_ads[0] = 0.0;
|
||||
c_vals_ads[1] = 2.0;
|
||||
c_vals_ads[2] = 8.0;
|
||||
std::vector<double> ads_vals(3, -1e100);
|
||||
ads_vals[0] = 0.0;
|
||||
ads_vals[1] = 0.0015;
|
||||
ads_vals[2] = 0.0025;
|
||||
// ads_vals[1] = 0.0;
|
||||
// ads_vals[2] = 0.0;
|
||||
std::vector<double> water_vel_vals(2, -1e100);
|
||||
water_vel_vals[0] = 0.0;
|
||||
water_vel_vals[1] = 10.0;
|
||||
std::vector<double> shear_vrf_vals(2, -1e100);
|
||||
shear_vrf_vals[0] = 1.0;
|
||||
shear_vrf_vals[1] = 1.0;
|
||||
poly_props.set(c_max, mix_param, rock_density, dead_pore_vol, res_factor, c_max_ads,
|
||||
static_cast<Opm::PolymerProperties::AdsorptionBehaviour>(ads_index),
|
||||
c_vals_visc, visc_mult_vals, c_vals_ads, ads_vals, water_vel_vals, shear_vrf_vals);
|
||||
}
|
||||
|
||||
// Warn if gravity but no density difference.
|
||||
bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0);
|
||||
if (use_gravity) {
|
||||
if (props->density()[0] == props->density()[1]) {
|
||||
std::cout << "**** Warning: nonzero gravity, but zero density difference." << std::endl;
|
||||
}
|
||||
}
|
||||
const double *grav = use_gravity ? &gravity[0] : 0;
|
||||
|
||||
// Initialising src
|
||||
int num_cells = grid->c_grid()->number_of_cells;
|
||||
std::vector<double> src(num_cells, 0.0);
|
||||
if (use_deck) {
|
||||
// Do nothing, wells will be the driving force, not source terms.
|
||||
} else {
|
||||
// Compute pore volumes, in order to enable specifying injection rate
|
||||
// terms of total pore volume.
|
||||
std::vector<double> porevol;
|
||||
if (rock_comp->isActive()) {
|
||||
computePorevolume(*grid->c_grid(), props->porosity(), *rock_comp, state.pressure(), porevol);
|
||||
} else {
|
||||
computePorevolume(*grid->c_grid(), props->porosity(), porevol);
|
||||
}
|
||||
const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0);
|
||||
const double default_injection = use_gravity ? 0.0 : 0.1;
|
||||
const double flow_per_sec = param.getDefault<double>("injected_porevolumes_per_day", default_injection)
|
||||
*tot_porevol_init/unit::day;
|
||||
src[0] = flow_per_sec;
|
||||
src[num_cells - 1] = -flow_per_sec;
|
||||
}
|
||||
|
||||
// Boundary conditions.
|
||||
FlowBCManager bcs;
|
||||
if (param.getDefault("use_pside", false)) {
|
||||
int pside = param.get<int>("pside");
|
||||
double pside_pressure = param.get<double>("pside_pressure");
|
||||
bcs.pressureSide(*grid->c_grid(), FlowBCManager::Side(pside), pside_pressure);
|
||||
}
|
||||
|
||||
// Linear solver.
|
||||
LinearSolverFactory linsolver(param);
|
||||
|
||||
// Write parameters used for later reference.
|
||||
bool output = param.getDefault("output", true);
|
||||
if (output) {
|
||||
std::string 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");
|
||||
}
|
||||
|
||||
|
||||
std::cout << "\n\n================ Starting main simulation loop ===============\n"
|
||||
<< std::flush;
|
||||
|
||||
SimulatorReport rep;
|
||||
if (!use_deck) {
|
||||
// Simple simulation without a deck.
|
||||
PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day,
|
||||
param.getDefault("poly_end_days", 800.0)*Opm::unit::day,
|
||||
param.getDefault("poly_amount", poly_props.cMax()));
|
||||
WellsManager wells;
|
||||
SimulatorPolymer simulator(param,
|
||||
*grid->c_grid(),
|
||||
*props,
|
||||
poly_props,
|
||||
rock_comp->isActive() ? rock_comp.get() : 0,
|
||||
wells,
|
||||
polymer_inflow,
|
||||
src,
|
||||
bcs.c_bcs(),
|
||||
linsolver,
|
||||
grav);
|
||||
SimulatorTimer simtimer;
|
||||
simtimer.init(param);
|
||||
warnIfUnusedParams(param);
|
||||
WellState well_state;
|
||||
well_state.init(0, state);
|
||||
rep = simulator.run(simtimer, state, well_state);
|
||||
} else {
|
||||
// With a deck, we may have more epochs etc.
|
||||
|
||||
WellState well_state;
|
||||
int step = 0;
|
||||
Opm::TimeMapPtr timeMap(new Opm::TimeMap(deck));
|
||||
SimulatorTimer simtimer;
|
||||
simtimer.init(timeMap);
|
||||
// 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.");
|
||||
}
|
||||
}
|
||||
for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) {
|
||||
simtimer.setCurrentStepNum(reportStepIdx);
|
||||
|
||||
// Report on start of report step.
|
||||
std::cout << "\n\n-------------- Starting report step " << reportStepIdx << " --------------"
|
||||
<< "\n (number of remaining steps: "
|
||||
<< simtimer.numSteps() - step << ")\n\n" << std::flush;
|
||||
|
||||
// Create new wells, polymer inflow controls.
|
||||
WellsManager wells(eclipseState , reportStepIdx , *grid->c_grid(), props->permeability());
|
||||
boost::scoped_ptr<PolymerInflowInterface> polymer_inflow;
|
||||
if (use_wpolymer) {
|
||||
if (wells.c_wells() == 0) {
|
||||
OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells.");
|
||||
}
|
||||
polymer_inflow.reset(new PolymerInflowFromDeck(deck, eclipseState, *wells.c_wells(), props->numCells(), simtimer.currentStepNum()));
|
||||
} else {
|
||||
polymer_inflow.reset(new PolymerInflowBasic(param.getDefault("poly_start_days", 300.0)*Opm::unit::day,
|
||||
param.getDefault("poly_end_days", 800.0)*Opm::unit::day,
|
||||
param.getDefault("poly_amount", poly_props.cMax())));
|
||||
}
|
||||
|
||||
// @@@ HACK: we should really make a new well state and
|
||||
// properly transfer old well state to it every report step,
|
||||
// since number of wells may change etc.
|
||||
if (reportStepIdx == 0) {
|
||||
well_state.init(wells.c_wells(), state);
|
||||
}
|
||||
|
||||
// Create and run simulator.
|
||||
SimulatorPolymer simulator(param,
|
||||
*grid->c_grid(),
|
||||
*props,
|
||||
poly_props,
|
||||
rock_comp->isActive() ? rock_comp.get() : 0,
|
||||
wells,
|
||||
*polymer_inflow,
|
||||
src,
|
||||
bcs.c_bcs(),
|
||||
linsolver,
|
||||
grav);
|
||||
if (reportStepIdx == 0) {
|
||||
warnIfUnusedParams(param);
|
||||
}
|
||||
SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state);
|
||||
|
||||
// Update total timing report and remember step number.
|
||||
rep += epoch_rep;
|
||||
step = simtimer.currentStepNum();
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\n\n================ End of simulation ===============\n\n";
|
||||
rep.report(std::cout);
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
std::cerr << "Program threw an exception: " << e.what() << "\n";
|
||||
throw;
|
||||
}
|
274
examples/sim_poly_fi2p_comp_ad.cpp
Normal file
274
examples/sim_poly_fi2p_comp_ad.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/core/pressure/FlowBCManager.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/simulator/initState.hpp>
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <opm/material/fluidmatrixinteractions/EclMaterialLawManager.hpp>
|
||||
#include <opm/core/io/eclipse/EclipseWriter.hpp>
|
||||
#include <opm/core/props/BlackoilPropertiesBasic.hpp>
|
||||
#include <opm/core/props/BlackoilPropertiesFromDeck.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/core/linalg/LinearSolverFactory.hpp>
|
||||
#include <opm/autodiff/NewtonIterationBlackoilSimple.hpp>
|
||||
#include <opm/autodiff/NewtonIterationBlackoilCPR.hpp>
|
||||
#include <opm/autodiff/createGlobalCellArray.hpp>
|
||||
|
||||
#include <opm/polymer/PolymerBlackoilState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
|
||||
#include <opm/polymer/fullyimplicit/SimulatorFullyImplicitCompressiblePolymer.hpp>
|
||||
#include <opm/polymer/fullyimplicit/PolymerPropsAd.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
#include <opm/polymer/PolymerInflow.hpp>
|
||||
#include <opm/polymer/PolymerState.hpp>
|
||||
|
||||
#include <opm/autodiff/BlackoilPropsAdFromDeck.hpp>
|
||||
#include <opm/autodiff/BlackoilPropsAdInterface.hpp>
|
||||
#include <opm/autodiff/GeoProps.hpp>
|
||||
#include <opm/autodiff/GridHelpers.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/OpmLog/OpmLog.hpp>
|
||||
#include <opm/parser/eclipse/OpmLog/StreamLog.hpp>
|
||||
#include <opm/parser/eclipse/OpmLog/CounterLog.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseMode.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/checkDeck.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
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<GridManager> grid;
|
||||
std::shared_ptr<BlackoilPropertiesInterface> props;
|
||||
std::shared_ptr<BlackoilPropsAdInterface> new_props;
|
||||
std::shared_ptr<RockCompressibility> 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<std::string>("deck_filename");
|
||||
|
||||
// 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 (...) {
|
||||
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::ParseMode parseMode({{ ParseMode::PARSE_RANDOM_SLASH , InputError::IGNORE }});
|
||||
Opm::ParserPtr parser(new Opm::Parser());
|
||||
{
|
||||
std::shared_ptr<Opm::StreamLog> streamLog = std::make_shared<Opm::StreamLog>(logFile , Opm::Log::DefaultMessageTypes);
|
||||
std::shared_ptr<Opm::CounterLog> counterLog = std::make_shared<Opm::CounterLog>(Opm::Log::DefaultMessageTypes);
|
||||
|
||||
Opm::OpmLog::addBackend( "STREAM" , streamLog );
|
||||
Opm::OpmLog::addBackend( "COUNTER" , counterLog );
|
||||
}
|
||||
|
||||
Opm::DeckConstPtr deck;
|
||||
std::shared_ptr<EclipseState> 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;
|
||||
}
|
||||
|
||||
// Grid init
|
||||
std::vector<double> 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);
|
||||
|
||||
// Rock and fluid init
|
||||
|
||||
std::vector<int> compressedToCartesianIdx;
|
||||
Opm::createGlobalCellArray(*grid->c_grid(), compressedToCartesianIdx);
|
||||
|
||||
typedef BlackoilPropsAdFromDeck::MaterialLawManager MaterialLawManager;
|
||||
auto materialLawManager = std::make_shared<MaterialLawManager>();
|
||||
materialLawManager->initFromDeck(deck, eclipseState, compressedToCartesianIdx);
|
||||
|
||||
props.reset(new BlackoilPropertiesFromDeck( deck, eclipseState, materialLawManager,
|
||||
Opm::UgGridHelpers::numCells(cGrid),
|
||||
Opm::UgGridHelpers::globalCell(cGrid),
|
||||
Opm::UgGridHelpers::cartDims(cGrid),
|
||||
param));
|
||||
new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, materialLawManager, cGrid));
|
||||
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<NewtonIterationBlackoilInterface> fis_solver;
|
||||
if (param.getDefault("use_cpr", true)) {
|
||||
fis_solver.reset(new NewtonIterationBlackoilCPR(param));
|
||||
} else {
|
||||
fis_solver.reset(new NewtonIterationBlackoilSimple(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;
|
||||
|
||||
Opm::BlackoilOutputWriter
|
||||
outputWriter(cGrid, param, eclipseState, pu,
|
||||
new_props->permeability());
|
||||
|
||||
SimulatorReport fullReport;
|
||||
// Create and run simulator.
|
||||
Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, grav);
|
||||
SimulatorFullyImplicitCompressiblePolymer<UnstructuredGrid>
|
||||
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(eclipseState, 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;
|
||||
}
|
||||
|
169
opm/polymer/CompressibleTpfaPolymer.cpp
Normal file
169
opm/polymer/CompressibleTpfaPolymer.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <opm/polymer/PolymerBlackoilState.hpp>
|
||||
#include <opm/polymer/CompressibleTpfaPolymer.hpp>
|
||||
#include <opm/core/props/BlackoilPropertiesInterface.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
#include <opm/core/pressure/tpfa/ifs_tpfa.h>
|
||||
#include <opm/core/pressure/tpfa/trans_tpfa.h>
|
||||
#include <opm/core/pressure/mimetic/mimetic.h>
|
||||
#include <opm/core/pressure/flow_bc.h>
|
||||
#include <opm/core/linalg/LinearSolverInterface.hpp>
|
||||
#include <opm/core/linalg/sparse_sys.h>
|
||||
#include <opm/polymer/polymerUtilities.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
|
||||
/// Construct solver
|
||||
/// \param[in] grid A 2d or 3d grid.
|
||||
/// \param[in] props Rock and fluid properties.
|
||||
/// \param[in] linsolver Linear solver to use.
|
||||
/// \param[in] residual_tol Solution accepted if inf-norm of residual is smaller.
|
||||
/// \param[in] change_tol Solution accepted if inf-norm of change in pressure is smaller.
|
||||
/// \param[in] maxiter Maximum acceptable number of iterations.
|
||||
/// \param[in] gravity Gravity vector. If non-null, the array should
|
||||
/// have D elements.
|
||||
/// \param[in] wells The wells argument. Will be used in solution,
|
||||
/// is ignored if NULL.
|
||||
/// Note: this class observes the well object, and
|
||||
/// makes the assumption that the well topology
|
||||
/// and completions does not change during the
|
||||
/// run. However, controls (only) are allowed
|
||||
/// to change.
|
||||
CompressibleTpfaPolymer::CompressibleTpfaPolymer(const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
const PolymerProperties& poly_props,
|
||||
const LinearSolverInterface& linsolver,
|
||||
const double residual_tol,
|
||||
const double change_tol,
|
||||
const int maxiter,
|
||||
const double* gravity,
|
||||
const Wells* wells)
|
||||
: CompressibleTpfa(grid, props, rock_comp_props, linsolver,
|
||||
residual_tol, change_tol, maxiter,
|
||||
gravity, wells),
|
||||
poly_props_(poly_props),
|
||||
c_(0),
|
||||
cmax_(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// Solve the pressure equation. The nonlinear equations ares
|
||||
/// solved by a Newton-Raphson scheme. May throw an exception if
|
||||
/// the number of iterations exceed maxiter (set in constructor).
|
||||
void CompressibleTpfaPolymer::solve(const double dt,
|
||||
PolymerBlackoilState& state,
|
||||
WellState& well_state)
|
||||
{
|
||||
c_ = &state.concentration();
|
||||
cmax_ = &state.maxconcentration();
|
||||
CompressibleTpfa::solve(dt, state, well_state);
|
||||
}
|
||||
|
||||
/// Compute per-solve dynamic properties.
|
||||
void CompressibleTpfaPolymer::computePerSolveDynamicData(const double /* dt */,
|
||||
const BlackoilState& state,
|
||||
const WellState& /* well_state */)
|
||||
{
|
||||
// std::vector<double> cell_relperm__;
|
||||
// std::vector<double> cell_eff_relperm_;
|
||||
const int nc = grid_.number_of_cells;
|
||||
const int np = props_.numPhases();
|
||||
cell_relperm_.resize(nc*np);
|
||||
cell_eff_viscosity_.resize(nc*np);
|
||||
const double* cell_s = &state.saturation()[0];
|
||||
props_.relperm(nc, cell_s, &allcells_[0], &cell_relperm_[0], 0);
|
||||
computeWellPotentials(state);
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), initial_porevol_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Compute per-iteration dynamic properties for cells.
|
||||
void CompressibleTpfaPolymer::computeCellDynamicData(const double /*dt*/,
|
||||
const BlackoilState& state,
|
||||
const WellState& /*well_state*/)
|
||||
{
|
||||
// These are the variables that get computed by this function:
|
||||
//
|
||||
// std::vector<double> cell_A_;
|
||||
// std::vector<double> cell_dA_;
|
||||
// std::vector<double> cell_viscosity_;
|
||||
// std::vector<double> cell_eff_viscosity_;
|
||||
// std::vector<double> cell_phasemob_;
|
||||
// std::vector<double> cell_voldisc_;
|
||||
// std::vector<double> porevol_; // Only modified if rock_comp_props_ is non-null.
|
||||
// std::vector<double> rock_comp_; // Empty unless rock_comp_props_ is non-null.
|
||||
const int nc = grid_.number_of_cells;
|
||||
const int np = props_.numPhases();
|
||||
const double* cell_p = &state.pressure()[0];
|
||||
const double* cell_T = &state.temperature()[0];
|
||||
const double* cell_z = &state.surfacevol()[0];
|
||||
cell_A_.resize(nc*np*np);
|
||||
cell_dA_.resize(nc*np*np);
|
||||
props_.matrix(nc, cell_p, cell_T, cell_z, &allcells_[0], &cell_A_[0], &cell_dA_[0]);
|
||||
cell_viscosity_.resize(nc*np);
|
||||
props_.viscosity(nc, cell_p, cell_T, cell_z, &allcells_[0], &cell_viscosity_[0], 0);
|
||||
cell_phasemob_.resize(nc*np);
|
||||
for (int cell = 0; cell < nc; ++cell) {
|
||||
poly_props_.effectiveVisc((*c_)[cell], &cell_viscosity_[np*cell + 0], cell_eff_viscosity_[np*cell + 0]);
|
||||
poly_props_.effectiveMobilities((*c_)[cell], (*cmax_)[cell], &cell_viscosity_[np*cell + 0], &cell_relperm_[np*cell + 0], &cell_phasemob_[np*cell + 0]);
|
||||
}
|
||||
|
||||
// Volume discrepancy: we have that
|
||||
// z = Au, voldiscr = sum(u) - 1,
|
||||
// but I am not sure it is actually needed.
|
||||
// Use zero for now.
|
||||
// TODO: Check this!
|
||||
cell_voldisc_.clear();
|
||||
cell_voldisc_.resize(nc, 0.0);
|
||||
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol_);
|
||||
rock_comp_.resize(nc);
|
||||
for (int cell = 0; cell < nc; ++cell) {
|
||||
rock_comp_[cell] = rock_comp_props_->rockComp(state.pressure()[cell]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
111
opm/polymer/CompressibleTpfaPolymer.hpp
Normal file
111
opm/polymer/CompressibleTpfaPolymer.hpp
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_COMPRESSIBLETPFAPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_COMPRESSIBLETPFAPOLYMER_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/core/pressure/CompressibleTpfa.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
struct Wells;
|
||||
struct FlowBoundaryConditions;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class BlackoilState;
|
||||
class PolymerBlackoilState;
|
||||
class RockCompressibility;
|
||||
class PolymerProperties;
|
||||
class LinearSolverInterface;
|
||||
class PolymerBlackoilState;
|
||||
class WellState;
|
||||
|
||||
/// Encapsulating a tpfa pressure solver for the compressible-fluid case with polymer.
|
||||
/// Supports gravity, wells controlled by bhp or reservoir rates,
|
||||
/// boundary conditions and simple sources as driving forces.
|
||||
/// Below we use the shortcuts D for the number of dimensions, N
|
||||
/// for the number of cells and F for the number of faces.
|
||||
class CompressibleTpfaPolymer : public CompressibleTpfa
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct solver, possibly with rock compressibility.
|
||||
/// \param[in] grid A 2d or 3d grid.
|
||||
/// \param[in] props Rock and fluid properties.
|
||||
/// \param[in] rock_comp_props Rock compressibility properties. May be null.
|
||||
/// \param[in] poly_props Polymer properties.
|
||||
/// \param[in] linsolver Linear solver to use.
|
||||
/// \param[in] residual_tol Solution accepted if inf-norm of residual is smaller.
|
||||
/// \param[in] change_tol Solution accepted if inf-norm of change in pressure is smaller.
|
||||
/// \param[in] maxiter Maximum acceptable number of iterations.
|
||||
/// \param[in] gravity Gravity vector. If non-null, the array should
|
||||
/// have D elements.
|
||||
/// \param[in] wells The wells argument. Will be used in solution,
|
||||
/// is ignored if NULL.
|
||||
/// Note: this class observes the well object, and
|
||||
/// makes the assumption that the well topology
|
||||
/// and completions does not change during the
|
||||
/// run. However, controls (only) are allowed
|
||||
/// to change.
|
||||
CompressibleTpfaPolymer(const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
const PolymerProperties& poly_props,
|
||||
const LinearSolverInterface& linsolver,
|
||||
const double residual_tol,
|
||||
const double change_tol,
|
||||
const int maxiter,
|
||||
const double* gravity,
|
||||
const Wells* wells);
|
||||
|
||||
/// Solve the pressure equation. The nonlinear equations ares solved by a
|
||||
/// Newton-Raphson scheme.
|
||||
/// May throw an exception if the number of iterations
|
||||
/// exceed maxiter (set in constructor).
|
||||
void solve(const double dt,
|
||||
PolymerBlackoilState& state,
|
||||
WellState& well_state);
|
||||
|
||||
private:
|
||||
virtual void computeCellDynamicData(const double dt,
|
||||
const BlackoilState& state,
|
||||
const WellState& well_state);
|
||||
|
||||
virtual void computePerSolveDynamicData(const double dt,
|
||||
const BlackoilState& state,
|
||||
const WellState& well_state);
|
||||
|
||||
|
||||
private:
|
||||
// ------ Data that will remain unmodified after construction. ------
|
||||
const PolymerProperties& poly_props_;
|
||||
// ------ Data that will be updated every solve() call. ------
|
||||
const std::vector<double>* c_;
|
||||
const std::vector<double>* cmax_;
|
||||
std::vector<double> cell_eff_viscosity_;
|
||||
std::vector<double> cell_relperm_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_COMPRESSIBLETPFAPOLYMER_HEADER_INCLUDED
|
74
opm/polymer/GravityColumnSolverPolymer.hpp
Normal file
74
opm/polymer/GravityColumnSolverPolymer.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_GRAVITYCOLUMNSOLVERPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_GRAVITYCOLUMNSOLVERPOLYMER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// Class for doing gravity segregation (only),
|
||||
/// on a vertical column of cells.
|
||||
template <class FluxModel, class Model>
|
||||
class GravityColumnSolverPolymer
|
||||
{
|
||||
public:
|
||||
/// Note: the model will be changed since it stores computed
|
||||
/// quantities in itself, such as mobilities.
|
||||
GravityColumnSolverPolymer(FluxModel& fmodel,
|
||||
const Model& model,
|
||||
const UnstructuredGrid& grid,
|
||||
const double tol,
|
||||
const int maxit);
|
||||
|
||||
/// \param[in] columns for each column (with logical cartesian indices as key),
|
||||
/// contains the cells on which to solve the segregation
|
||||
/// problem. For each column, its cells must be in a single
|
||||
/// vertical column, and ordered
|
||||
/// (direction doesn't matter).
|
||||
void solve(const std::vector<std::vector<int> >& columns,
|
||||
const double dt,
|
||||
std::vector<double>& s,
|
||||
std::vector<double>& c,
|
||||
std::vector<double>& cmax);
|
||||
|
||||
private:
|
||||
void solveSingleColumn(const std::vector<int>& column_cells,
|
||||
const double dt,
|
||||
std::vector<double>& s,
|
||||
std::vector<double>& c,
|
||||
std::vector<double>& cmax,
|
||||
std::vector<double>& sol_vec
|
||||
);
|
||||
FluxModel& fmodel_;
|
||||
const Model& model_;
|
||||
const UnstructuredGrid& grid_;
|
||||
const double tol_;
|
||||
const int maxit_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#include <opm/polymer/GravityColumnSolverPolymer_impl.hpp>
|
||||
|
||||
#endif // OPM_GRAVITYCOLUMNSOLVERPOLYMER_HEADER_INCLUDED
|
342
opm/polymer/GravityColumnSolverPolymer_impl.hpp
Normal file
342
opm/polymer/GravityColumnSolverPolymer_impl.hpp
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <opm/polymer/GravityColumnSolverPolymer.hpp>
|
||||
#include <opm/core/linalg/blas_lapack.h>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
template <class FluxModel, class Model>
|
||||
GravityColumnSolverPolymer<FluxModel, Model>::GravityColumnSolverPolymer(FluxModel& fmodel,
|
||||
const Model& model,
|
||||
const UnstructuredGrid& grid,
|
||||
const double tol,
|
||||
const int maxit)
|
||||
: fmodel_(fmodel), model_(model), grid_(grid), tol_(tol), maxit_(maxit)
|
||||
{
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct ZeroVec
|
||||
{
|
||||
double operator[](int) const { return 0.0; }
|
||||
};
|
||||
struct StateWithZeroFlux
|
||||
{
|
||||
StateWithZeroFlux(std::vector<double>& s, std::vector<double>& c, std::vector<double>& cmax_arg) : sat(s), cpoly(c), cmax(cmax_arg) {}
|
||||
const ZeroVec& faceflux() const { return zv; }
|
||||
const std::vector<double>& saturation() const { return sat; }
|
||||
std::vector<double>& saturation() { return sat; }
|
||||
const std::vector<double>& concentration() const { return cpoly; }
|
||||
std::vector<double>& concentration() { return cpoly; }
|
||||
const std::vector<double>& maxconcentration() const { return cmax; }
|
||||
std::vector<double>& maxconcentration() { return cmax; }
|
||||
ZeroVec zv;
|
||||
std::vector<double>& sat;
|
||||
std::vector<double>& cpoly;
|
||||
std::vector<double>& cmax;
|
||||
};
|
||||
|
||||
struct Vecs
|
||||
{
|
||||
Vecs(int sz) : sol(sz, 0.0) {}
|
||||
const std::vector<double>& solution() const { return sol; }
|
||||
std::vector<double>& writableSolution() { return sol; }
|
||||
std::vector<double> sol;
|
||||
};
|
||||
struct JacSys
|
||||
{
|
||||
JacSys(int sz) : v(sz) {}
|
||||
const Vecs& vector() const { return v; }
|
||||
Vecs& vector() { return v; }
|
||||
Vecs v;
|
||||
typedef std::vector<double> vector_type;
|
||||
};
|
||||
|
||||
struct BandMatrixCoeff
|
||||
{
|
||||
BandMatrixCoeff(int N, int ku, int kl) : N_(N), ku_(ku), kl_(kl), nrow_(2*kl + ku + 1) {
|
||||
}
|
||||
|
||||
|
||||
// compute the position where to store the coefficient of a matrix A_{i,j} (i,j=0,...,N-1)
|
||||
// in a array which is sent to the band matrix solver of LAPACK.
|
||||
int operator ()(int i, int j) const {
|
||||
return kl_ + ku_ + i - j + j*nrow_;
|
||||
}
|
||||
|
||||
const int N_;
|
||||
const int ku_;
|
||||
const int kl_;
|
||||
const int nrow_;
|
||||
};
|
||||
|
||||
} // anon namespace
|
||||
|
||||
|
||||
|
||||
/// \param[in] columns for each column col, columns[col]
|
||||
/// contains the cells on which to solve the segregation
|
||||
/// problem. For each column, its cells must be in a single
|
||||
/// vertical column, connected and ordered
|
||||
/// (direction doesn't matter).
|
||||
template <class FluxModel, class Model>
|
||||
void GravityColumnSolverPolymer<FluxModel, Model>::solve(const std::vector<std::vector<int> >& columns,
|
||||
const double dt,
|
||||
std::vector<double>& s,
|
||||
std::vector<double>& c,
|
||||
std::vector<double>& cmax
|
||||
)
|
||||
{
|
||||
// Initialize model. These things are done for the whole grid!
|
||||
StateWithZeroFlux state(s, c, cmax); // This holds s, c and cmax by reference.
|
||||
JacSys sys(2*grid_.number_of_cells);
|
||||
std::vector<double> increment(2*grid_.number_of_cells, 0.0);
|
||||
fmodel_.initStep(state, grid_, sys);
|
||||
|
||||
int iter = 0;
|
||||
double max_delta = 1e100;
|
||||
const double cmax_cell = 2.0*model_.cMax();
|
||||
const double tol_c_cell = 1e-2*cmax_cell;
|
||||
while (iter < maxit_) {
|
||||
fmodel_.initIteration(state, grid_, sys);
|
||||
int size = columns.size();
|
||||
for(int i = 0; i < size; ++i) {
|
||||
solveSingleColumn(columns[i], dt, s, c, cmax, increment);
|
||||
}
|
||||
for (int cell = 0; cell < grid_.number_of_cells; ++cell) {
|
||||
double& s_cell = sys.vector().writableSolution()[2*cell + 0];
|
||||
double& c_cell = sys.vector().writableSolution()[2*cell + 1];
|
||||
s_cell += increment[2*cell + 0];
|
||||
c_cell += increment[2*cell + 1];
|
||||
if (s_cell < 0.) {
|
||||
double& incr = increment[2*cell + 0];
|
||||
s_cell -= incr;
|
||||
if (std::fabs(incr) < 1e-2) {
|
||||
incr = -s_cell;
|
||||
s_cell = 0.;
|
||||
} else {
|
||||
incr = -s_cell/2.0;
|
||||
s_cell = s_cell/2.0;
|
||||
}
|
||||
}
|
||||
if (s_cell > 1.) {
|
||||
double& incr = increment[2*cell + 0];
|
||||
s_cell -= incr;
|
||||
if (std::fabs(incr) < 1e-2) {
|
||||
incr = 1. - s_cell;
|
||||
s_cell = 1.;
|
||||
} else {
|
||||
incr = (1 - s_cell)/2.0;
|
||||
s_cell = (1 + s_cell)/2.0;
|
||||
}
|
||||
}
|
||||
if (c_cell < 0.) {
|
||||
double& incr = increment[2*cell + 1];
|
||||
c_cell -= incr;
|
||||
if (std::fabs(incr) < tol_c_cell) {
|
||||
incr = -c_cell;
|
||||
c_cell = 0.;
|
||||
} else {
|
||||
incr = -c_cell/2.0;
|
||||
c_cell = c_cell/2.0;
|
||||
}
|
||||
}
|
||||
if (c_cell > cmax_cell) {
|
||||
double& incr = increment[2*cell + 1];
|
||||
c_cell -= incr;
|
||||
if (std::fabs(incr) < tol_c_cell) {
|
||||
incr = cmax_cell - c_cell;
|
||||
c_cell = cmax_cell;
|
||||
} else {
|
||||
incr = (cmax_cell - c_cell)/2.0;
|
||||
c_cell = (cmax_cell + c_cell)/2.0;
|
||||
}
|
||||
}
|
||||
|
||||
// if (s_cell < 0.) {
|
||||
// increment[2*cell + 0] = increment[2*cell + 0] - s_cell;
|
||||
// s_cell = 0.;
|
||||
// } else if (s_cell > 1.) {
|
||||
// increment[2*cell + 0] = increment[2*cell + 0] - s_cell + 1.;
|
||||
// s_cell = 1.;
|
||||
// }
|
||||
// if (c_cell < 0) {
|
||||
// increment[2*cell + 1] = increment[2*cell + 1] - c_cell;
|
||||
// c_cell = 0.;
|
||||
// } else if (c_cell > cmax_cell) {
|
||||
// increment[2*cell + 1] = increment[2*cell + 1] - c_cell + cmax_cell;
|
||||
// c_cell = cmax_cell;
|
||||
// }
|
||||
}
|
||||
const double maxelem = *std::max_element(increment.begin(), increment.end());
|
||||
const double minelem = *std::min_element(increment.begin(), increment.end());
|
||||
max_delta = std::max(maxelem, -minelem);
|
||||
std::cout << "Iteration " << iter << " max_delta = " << max_delta << std::endl;
|
||||
if (max_delta < tol_) {
|
||||
break;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
if (max_delta >= tol_) {
|
||||
OPM_THROW(std::runtime_error, "Failed to converge!");
|
||||
}
|
||||
// Finalize.
|
||||
// fmodel_.finishIteration(); //
|
||||
// finishStep() writes to state, which holds s by reference.
|
||||
// This will update the entire grid's state... cmax is updated here.
|
||||
fmodel_.finishStep(grid_, sys.vector().solution(), state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// \param[in] column_cells the cells on which to solve the segregation
|
||||
/// problem. Must be in a single vertical column,
|
||||
/// and ordered (direction doesn't matter).
|
||||
template <class FluxModel, class Model>
|
||||
void GravityColumnSolverPolymer<FluxModel, Model>::solveSingleColumn(const std::vector<int>& column_cells,
|
||||
const double dt,
|
||||
std::vector<double>& s,
|
||||
std::vector<double>& c,
|
||||
std::vector<double>& cmax,
|
||||
std::vector<double>& sol_vec)
|
||||
{
|
||||
// This is written only to work with SinglePointUpwindTwoPhase,
|
||||
// not with arbitrary problem models.
|
||||
int col_size = column_cells.size();
|
||||
|
||||
// if (col_size == 1) {
|
||||
// sol_vec[2*column_cells[0] + 0] = 0.0;
|
||||
// sol_vec[2*column_cells[0] + 1] = 0.0;
|
||||
// return;
|
||||
// }
|
||||
|
||||
StateWithZeroFlux state(s, c, cmax); // This holds s by reference.
|
||||
|
||||
// Assemble.
|
||||
const int kl = 3;
|
||||
const int ku = 3;
|
||||
const int nrow = 2*kl + ku + 1;
|
||||
const int N = 2*col_size; // N unknowns: s and c for each cell.
|
||||
std::vector<double> hm(nrow*N, 0.0); // band matrix with 3 upper and 3 lower diagonals.
|
||||
std::vector<double> rhs(N, 0.0);
|
||||
const BandMatrixCoeff bmc(N, ku, kl);
|
||||
|
||||
|
||||
for (int ci = 0; ci < col_size; ++ci) {
|
||||
std::vector<double> F(2, 0.);
|
||||
std::vector<double> dFd1(4, 0.);
|
||||
std::vector<double> dFd2(4, 0.);
|
||||
std::vector<double> dF(4, 0.);
|
||||
const int cell = column_cells[ci];
|
||||
const int prev_cell = (ci == 0) ? -999 : column_cells[ci - 1];
|
||||
const int next_cell = (ci == col_size - 1) ? -999 : column_cells[ci + 1];
|
||||
// model_.initResidual(cell, F);
|
||||
for (int j = grid_.cell_facepos[cell]; j < grid_.cell_facepos[cell+1]; ++j) {
|
||||
const int face = grid_.cell_faces[j];
|
||||
const int c1 = grid_.face_cells[2*face + 0];
|
||||
const int c2 = grid_.face_cells[2*face + 1];
|
||||
if (c1 == prev_cell || c2 == prev_cell || c1 == next_cell || c2 == next_cell) {
|
||||
F.assign(2, 0.);
|
||||
dFd1.assign(4, 0.);
|
||||
dFd2.assign(4, 0.);
|
||||
fmodel_.fluxConnection(state, grid_, dt, cell, face, &F[0], &dFd1[0], &dFd2[0]);
|
||||
if (c1 == prev_cell || c2 == prev_cell) {
|
||||
hm[bmc(2*ci + 0, 2*(ci - 1) + 0)] += dFd2[0];
|
||||
hm[bmc(2*ci + 0, 2*(ci - 1) + 1)] += dFd2[1];
|
||||
hm[bmc(2*ci + 1, 2*(ci - 1) + 0)] += dFd2[2];
|
||||
hm[bmc(2*ci + 1, 2*(ci - 1) + 1)] += dFd2[3];
|
||||
} else {
|
||||
assert(c1 == next_cell || c2 == next_cell);
|
||||
hm[bmc(2*ci + 0, 2*(ci + 1) + 0)] += dFd2[0];
|
||||
hm[bmc(2*ci + 0, 2*(ci + 1) + 1)] += dFd2[1];
|
||||
hm[bmc(2*ci + 1, 2*(ci + 1) + 0)] += dFd2[2];
|
||||
hm[bmc(2*ci + 1, 2*(ci + 1) + 1)] += dFd2[3];
|
||||
}
|
||||
hm[bmc(2*ci + 0, 2*ci + 0)] += dFd1[0];
|
||||
hm[bmc(2*ci + 0, 2*ci + 1)] += dFd1[1];
|
||||
hm[bmc(2*ci + 1, 2*ci + 0)] += dFd1[2];
|
||||
hm[bmc(2*ci + 1, 2*ci + 1)] += dFd1[3];
|
||||
|
||||
rhs[2*ci + 0] += F[0];
|
||||
rhs[2*ci + 1] += F[1];
|
||||
}
|
||||
}
|
||||
F.assign(2, 0.);
|
||||
dF.assign(4, 0.);
|
||||
fmodel_.accumulation(grid_, cell, &F[0], &dF[0]);
|
||||
hm[bmc(2*ci + 0, 2*ci + 0)] += dF[0];
|
||||
hm[bmc(2*ci + 0, 2*ci + 1)] += dF[1];
|
||||
hm[bmc(2*ci + 1, 2*ci + 0)] += dF[2];
|
||||
if (std::abs(dF[3]) < 1e-12) {
|
||||
hm[bmc(2*ci + 1, 2*ci + 1)] += 1e-12;
|
||||
} else {
|
||||
hm[bmc(2*ci + 1, 2*ci + 1)] += dF[3];
|
||||
}
|
||||
|
||||
rhs[2*ci + 0] += F[0];
|
||||
rhs[2*ci + 1] += F[1];
|
||||
|
||||
}
|
||||
// model_.sourceTerms(); // Not needed
|
||||
// Solve.
|
||||
const int num_rhs = 1;
|
||||
int info = 0;
|
||||
std::vector<int> ipiv(N, 0);
|
||||
// Solution will be written to rhs.
|
||||
dgbsv_(&N, &kl, &ku, &num_rhs, &hm[0], &nrow, &ipiv[0], &rhs[0], &N, &info);
|
||||
if (info != 0) {
|
||||
std::cerr << "Failed column cells: ";
|
||||
std::copy(column_cells.begin(), column_cells.end(), std::ostream_iterator<int>(std::cerr, " "));
|
||||
std::cerr << "\n";
|
||||
OPM_THROW(std::runtime_error, "Lapack reported error in dgtsv: " << info);
|
||||
}
|
||||
for (int ci = 0; ci < col_size; ++ci) {
|
||||
sol_vec[2*column_cells[ci] + 0] = -rhs[2*ci + 0];
|
||||
sol_vec[2*column_cells[ci] + 1] = -rhs[2*ci + 1];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Opm
|
144
opm/polymer/IncompPropertiesDefaultPolymer.hpp
Normal file
144
opm/polymer/IncompPropertiesDefaultPolymer.hpp
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_INCOMPPROPERTIESDEFAULTPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_INCOMPPROPERTIESDEFAULTPOLYMER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/props/IncompPropertiesBasic.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/utility/linearInterpolation.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class IncompPropertiesDefaultPolymer : public Opm::IncompPropertiesBasic
|
||||
{
|
||||
public:
|
||||
/// Construct from parameters.
|
||||
/// The following parameters are accepted (defaults):
|
||||
/// num_phases (2) Must be 1 or 2.
|
||||
/// relperm_func ("Linear") Must be "Constant", "Linear" or "Quadratic".
|
||||
/// rho1 [rho2, rho3] (1.0e3) Density in kg/m^3
|
||||
/// mu1 [mu2, mu3] (1.0) Viscosity in cP
|
||||
/// porosity (1.0) Porosity
|
||||
/// permeability (100.0) Permeability in mD
|
||||
IncompPropertiesDefaultPolymer(const Opm::parameter::ParameterGroup& param, int dim, int num_cells)
|
||||
: Opm::IncompPropertiesBasic(param, dim, num_cells)
|
||||
{
|
||||
assert(numPhases() == 2);
|
||||
sw_.resize(3);
|
||||
sw_[0] = 0.2;
|
||||
sw_[1] = 0.7;
|
||||
sw_[2] = 1.0;
|
||||
krw_.resize(3);
|
||||
krw_[0] = 0.0;
|
||||
krw_[1] = 0.7;
|
||||
krw_[2] = 1.0;
|
||||
so_.resize(2);
|
||||
so_[0] = 0.3;
|
||||
so_[1] = 0.8;
|
||||
kro_.resize(2);
|
||||
kro_[0] = 0.0;
|
||||
kro_[1] = 1.0;
|
||||
}
|
||||
|
||||
/// \param[in] n Number of data points.
|
||||
/// \param[in] s Array of nP saturation values.
|
||||
/// \param[in] cells Array of n cell indices to be associated with the s values.
|
||||
/// \param[out] kr Array of nP relperm values, array must be valid before calling.
|
||||
/// \param[out] dkrds If non-null: array of nP^2 relperm derivative values,
|
||||
/// array must be valid before calling.
|
||||
/// The P^2 derivative matrix is
|
||||
/// m_{ij} = \frac{dkr_i}{ds^j},
|
||||
/// and is output in Fortran order (m_00 m_10 m_20 m_01 ...)
|
||||
|
||||
virtual void relperm(const int n,
|
||||
const double* s,
|
||||
const int* /*cells*/,
|
||||
double* kr,
|
||||
double* dkrds) const
|
||||
{
|
||||
// assert(dkrds == 0);
|
||||
// We assume two phases flow
|
||||
for (int i = 0; i < n; ++i) {
|
||||
kr[2*i] = krw(s[2*i]);
|
||||
kr[2*i+1] = kro(s[2*i+1]);
|
||||
if (dkrds != 0) {
|
||||
dkrds[4*i + 0] = krw_dsw(s[2*i]);
|
||||
dkrds[4*i + 3] = kro_dso(s[2*i+1]);
|
||||
dkrds[4*i + 1] = 0.0;
|
||||
dkrds[4*i + 2] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtain the range of allowable saturation values.
|
||||
/// In cell cells[i], saturation of phase p is allowed to be
|
||||
/// in the interval [smin[i*P + p], smax[i*P + p]].
|
||||
/// \param[in] n Number of data points.
|
||||
/// \param[in] cells Array of n cell indices.
|
||||
/// \param[out] smin Array of nP minimum s values, array must be valid before calling.
|
||||
/// \param[out] smax Array of nP maximum s values, array must be valid before calling.
|
||||
virtual void satRange(const int n,
|
||||
const int* /*cells*/,
|
||||
double* smin,
|
||||
double* smax) const
|
||||
{
|
||||
const int np = 2;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
smin[np*i + 0] = sw_[0];
|
||||
smax[np*i + 0] = sw_.back();
|
||||
smin[np*i + 1] = 1.0 - sw_[0];
|
||||
smax[np*i + 1] = 1.0 - sw_.back();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
double krw(double s) const
|
||||
{
|
||||
return Opm::linearInterpolation(sw_, krw_, s);
|
||||
}
|
||||
|
||||
double krw_dsw(double s) const
|
||||
{
|
||||
return Opm::linearInterpolationDerivative(sw_, krw_, s);
|
||||
}
|
||||
|
||||
|
||||
double kro(double s) const
|
||||
{
|
||||
return Opm::linearInterpolation(so_, kro_, s);
|
||||
}
|
||||
|
||||
double kro_dso(double s) const
|
||||
{
|
||||
return Opm::linearInterpolationDerivative(so_, kro_, s);
|
||||
}
|
||||
|
||||
std::vector<double> sw_;
|
||||
std::vector<double> krw_;
|
||||
std::vector<double> so_;
|
||||
std::vector<double> kro_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_INCOMPPROPERTIESDEFAULTPOLYMER_HEADER_INCLUDED
|
170
opm/polymer/IncompTpfaPolymer.cpp
Normal file
170
opm/polymer/IncompTpfaPolymer.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <opm/polymer/IncompTpfaPolymer.hpp>
|
||||
|
||||
#include <opm/core/props/IncompPropertiesInterface.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
#include <opm/core/pressure/tpfa/ifs_tpfa.h>
|
||||
#include <opm/core/pressure/tpfa/trans_tpfa.h>
|
||||
#include <opm/core/pressure/mimetic/mimetic.h>
|
||||
#include <opm/core/pressure/flow_bc.h>
|
||||
#include <opm/core/linalg/LinearSolverInterface.hpp>
|
||||
#include <opm/core/linalg/sparse_sys.h>
|
||||
#include <opm/polymer/PolymerState.hpp>
|
||||
#include <opm/polymer/polymerUtilities.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
|
||||
/// Construct solver, possibly with rock compressibility.
|
||||
/// \param[in] grid A 2d or 3d grid.
|
||||
/// \param[in] props Rock and fluid properties.
|
||||
/// \param[in] rock_comp_props Rock compressibility properties. May be null.
|
||||
/// \param[in] linsolver Linear solver to use.
|
||||
/// \param[in] residual_tol Solution accepted if inf-norm of residual is smaller.
|
||||
/// \param[in] change_tol Solution accepted if inf-norm of change in pressure is smaller.
|
||||
/// \param[in] maxiter Maximum acceptable number of iterations.
|
||||
/// \param[in] gravity Gravity vector. If non-null, the array should
|
||||
/// have D elements.
|
||||
/// \param[in] wells The wells argument. Will be used in solution,
|
||||
/// is ignored if NULL.
|
||||
/// Note: this class observes the well object, and
|
||||
/// makes the assumption that the well topology
|
||||
/// and completions does not change during the
|
||||
/// run. However, controls (only) are allowed
|
||||
/// to change.
|
||||
/// \param[in] src Source terms. May be empty().
|
||||
/// \param[in] bcs Boundary conditions, treat as all noflow if null.
|
||||
IncompTpfaPolymer::IncompTpfaPolymer(const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
const PolymerProperties& poly_props,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double residual_tol,
|
||||
const double change_tol,
|
||||
const int maxiter,
|
||||
const double* gravity,
|
||||
const Wells* wells,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs)
|
||||
: IncompTpfa(grid, props, rock_comp_props, linsolver,
|
||||
residual_tol, change_tol, maxiter,
|
||||
gravity, wells, src, bcs),
|
||||
poly_props_(poly_props),
|
||||
c_(0),
|
||||
cmax_(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// Solve the pressure equation. If there is no pressure
|
||||
/// dependency introduced by rock compressibility effects,
|
||||
/// the equation is linear, and it is solved directly.
|
||||
/// Otherwise, the nonlinear equations ares solved by a
|
||||
/// Newton-Raphson scheme.
|
||||
/// May throw an exception if the number of iterations
|
||||
/// exceed maxiter (set in constructor).
|
||||
void IncompTpfaPolymer::solve(const double dt,
|
||||
PolymerState& state,
|
||||
WellState& well_state)
|
||||
{
|
||||
c_ = &state.concentration();
|
||||
cmax_ = &state.maxconcentration();
|
||||
if (rock_comp_props_ != 0 && rock_comp_props_->isActive()) {
|
||||
solveRockComp(dt, state.twophaseState(), well_state);
|
||||
} else {
|
||||
solveIncomp(dt, state.twophaseState(), well_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Compute per-solve dynamic properties.
|
||||
void IncompTpfaPolymer::computePerSolveDynamicData(const double /*dt*/,
|
||||
const TwophaseState& state,
|
||||
const WellState& /*well_state*/)
|
||||
{
|
||||
// Computed here:
|
||||
//
|
||||
// std::vector<double> wdp_;
|
||||
// std::vector<double> totmob_;
|
||||
// std::vector<double> omega_;
|
||||
// std::vector<double> trans_;
|
||||
// std::vector<double> gpress_omegaweighted_;
|
||||
// std::vector<double> initial_porevol_;
|
||||
// ifs_tpfa_forces forces_;
|
||||
|
||||
// The only difference from IncompTpfa::computePerSolveDynamicData() is that
|
||||
// we call the polymer-aware versions of the computeTotalMobility*() functions.
|
||||
|
||||
// wdp_
|
||||
if (wells_) {
|
||||
Opm::computeWDP(*wells_, grid_, state.saturation(), props_.density(),
|
||||
gravity_ ? gravity_[2] : 0.0, true, wdp_);
|
||||
}
|
||||
// totmob_, omega_, gpress_omegaweighted_
|
||||
if (gravity_) {
|
||||
computeTotalMobilityOmega(props_, poly_props_, allcells_, state.saturation(), *c_, *cmax_,
|
||||
totmob_, omega_);
|
||||
mim_ip_density_update(grid_.number_of_cells, grid_.cell_facepos,
|
||||
&omega_[0],
|
||||
&gpress_[0], &gpress_omegaweighted_[0]);
|
||||
} else {
|
||||
computeTotalMobility(props_, poly_props_, allcells_, state.saturation(), *c_, *cmax_, totmob_);
|
||||
}
|
||||
// trans_
|
||||
tpfa_eff_trans_compute(const_cast<UnstructuredGrid*>(&grid_), &totmob_[0], &htrans_[0], &trans_[0]);
|
||||
// initial_porevol_
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), initial_porevol_);
|
||||
}
|
||||
// forces_
|
||||
forces_.src = src_.empty() ? NULL : &src_[0];
|
||||
forces_.bc = bcs_;
|
||||
forces_.W = wells_;
|
||||
forces_.totmob = &totmob_[0];
|
||||
forces_.wdp = wdp_.empty() ? NULL : &wdp_[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
111
opm/polymer/IncompTpfaPolymer.hpp
Normal file
111
opm/polymer/IncompTpfaPolymer.hpp
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_INCOMPTPFAPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_INCOMPTPFAPOLYMER_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/core/pressure/IncompTpfa.hpp>
|
||||
#include <vector>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
struct Wells;
|
||||
struct FlowBoundaryConditions;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class IncompPropertiesInterface;
|
||||
class RockCompressibility;
|
||||
class PolymerProperties;
|
||||
class LinearSolverInterface;
|
||||
class PolymerState;
|
||||
class WellState;
|
||||
|
||||
/// Encapsulating a tpfa pressure solver for the incompressible-fluid case with polymer.
|
||||
/// Supports gravity, wells controlled by bhp or reservoir rates,
|
||||
/// boundary conditions and simple sources as driving forces.
|
||||
/// Rock compressibility can be included, and necessary nonlinear
|
||||
/// iterations are handled.
|
||||
/// Below we use the shortcuts D for the number of dimensions, N
|
||||
/// for the number of cells and F for the number of faces.
|
||||
class IncompTpfaPolymer : public IncompTpfa
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct solver, possibly with rock compressibility.
|
||||
/// \param[in] grid A 2d or 3d grid.
|
||||
/// \param[in] props Rock and fluid properties.
|
||||
/// \param[in] rock_comp_props Rock compressibility properties. May be null.
|
||||
/// \param[in] poly_props Polymer properties.
|
||||
/// \param[in] linsolver Linear solver to use.
|
||||
/// \param[in] residual_tol Solution accepted if inf-norm of residual is smaller.
|
||||
/// \param[in] change_tol Solution accepted if inf-norm of change in pressure is smaller.
|
||||
/// \param[in] maxiter Maximum acceptable number of iterations.
|
||||
/// \param[in] gravity Gravity vector. If non-null, the array should
|
||||
/// have D elements.
|
||||
/// \param[in] wells The wells argument. Will be used in solution,
|
||||
/// is ignored if NULL.
|
||||
/// Note: this class observes the well object, and
|
||||
/// makes the assumption that the well topology
|
||||
/// and completions does not change during the
|
||||
/// run. However, controls (only) are allowed
|
||||
/// to change.
|
||||
/// \param[in] src Source terms. May be empty().
|
||||
/// \param[in] bcs Boundary conditions, treat as all noflow if null.
|
||||
IncompTpfaPolymer(const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
const PolymerProperties& poly_props,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double residual_tol,
|
||||
const double change_tol,
|
||||
const int maxiter,
|
||||
const double* gravity,
|
||||
const Wells* wells,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs);
|
||||
|
||||
|
||||
/// Solve the pressure equation. If there is no pressure
|
||||
/// dependency introduced by rock compressibility effects,
|
||||
/// the equation is linear, and it is solved directly.
|
||||
/// Otherwise, the nonlinear equations ares solved by a
|
||||
/// Newton-Raphson scheme.
|
||||
/// May throw an exception if the number of iterations
|
||||
/// exceed maxiter (set in constructor).
|
||||
void solve(const double dt,
|
||||
PolymerState& state,
|
||||
WellState& well_state);
|
||||
|
||||
private:
|
||||
virtual void computePerSolveDynamicData(const double dt,
|
||||
const TwophaseState& state,
|
||||
const WellState& well_state);
|
||||
private:
|
||||
// ------ Data that will remain unmodified after construction. ------
|
||||
const PolymerProperties& poly_props_;
|
||||
// ------ Data that will be updated every solve() call. ------
|
||||
const std::vector<double>* c_;
|
||||
const std::vector<double>* cmax_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_INCOMPTPFAPOLYMER_HEADER_INCLUDED
|
110
opm/polymer/Point2D.hpp
Normal file
110
opm/polymer/Point2D.hpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2015 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_POINT2D_HEADER_INCLUDED
|
||||
#define OPM_POINT2D_HEADER_INCLUDED
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
||||
class Point2D
|
||||
{
|
||||
public:
|
||||
Point2D(const double xi, const double yi)
|
||||
: x_(xi),
|
||||
y_(yi) {
|
||||
|
||||
}
|
||||
|
||||
Point2D()
|
||||
{
|
||||
}
|
||||
|
||||
double getX() const
|
||||
{
|
||||
return x_;
|
||||
}
|
||||
|
||||
double getY() const
|
||||
{
|
||||
return y_;
|
||||
}
|
||||
|
||||
void setX(const double x)
|
||||
{
|
||||
x_ = x;
|
||||
}
|
||||
|
||||
void setY(const double y)
|
||||
{
|
||||
y_ = y;
|
||||
}
|
||||
|
||||
/// Finding the intersection point of a line segment and a line.
|
||||
/// return true, if found.
|
||||
static bool findIntersection(Point2D line_segment1[2], Point2D line2[2], Point2D& intersection_point)
|
||||
{
|
||||
|
||||
const double x1 = line_segment1[0].getX();
|
||||
const double y1 = line_segment1[0].getY();
|
||||
const double x2 = line_segment1[1].getX();
|
||||
const double y2 = line_segment1[1].getY();
|
||||
|
||||
const double x3 = line2[0].getX();
|
||||
const double y3 = line2[0].getY();
|
||||
const double x4 = line2[1].getX();
|
||||
const double y4 = line2[1].getY();
|
||||
|
||||
const double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||
|
||||
if (d == 0.) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const double x = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
|
||||
const double y = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
|
||||
|
||||
if (x >= std::min(x1,x2) && x <= std::max(x1,x2)) {
|
||||
intersection_point.setX(x);
|
||||
intersection_point.setY(y);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
double x_;
|
||||
double y_;
|
||||
|
||||
}; // class Point2D
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_POINT2D_HEADER_INCLUDED
|
||||
|
64
opm/polymer/PolymerBlackoilState.hpp
Normal file
64
opm/polymer/PolymerBlackoilState.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_POLYMERBLACKOILSTATE_HEADER_INCLUDED
|
||||
#define OPM_POLYMERBLACKOILSTATE_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/core/simulator/BlackoilState.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// Simulator state for a compressible two-phase simulator with polymer.
|
||||
/// We use the Blackoil state parameters.
|
||||
class PolymerBlackoilState : public BlackoilState
|
||||
{
|
||||
public:
|
||||
void init(const UnstructuredGrid& g, int num_phases)
|
||||
{
|
||||
this->init(g.number_of_cells, g.number_of_faces, num_phases);
|
||||
}
|
||||
|
||||
void init(int number_of_cells, int number_of_faces, int num_phases)
|
||||
{
|
||||
BlackoilState::init(number_of_cells, number_of_faces, num_phases);
|
||||
concentration_.resize(number_of_cells, 0.0);
|
||||
cmax_.resize(number_of_cells, 0.0);
|
||||
}
|
||||
|
||||
std::vector<double>& concentration() { return concentration_; }
|
||||
std::vector<double>& maxconcentration() { return cmax_; }
|
||||
|
||||
const std::vector<double>& concentration() const { return concentration_; }
|
||||
const std::vector<double>& maxconcentration() const { return cmax_; }
|
||||
|
||||
private:
|
||||
std::vector<double> concentration_;
|
||||
std::vector<double> cmax_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // OPM_POLYMERBLACKOILSTATE_HEADER_INCLUDED
|
205
opm/polymer/PolymerInflow.cpp
Normal file
205
opm/polymer/PolymerInflow.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <opm/polymer/PolymerInflow.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/WellPolymerProperties.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/WellInjectionProperties.hpp>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
// ---------- Methods of PolymerInflowBasic ----------
|
||||
|
||||
/// Constructor.
|
||||
/// @param[in] starttime Start time of injection in seconds.
|
||||
/// @param[in] endtime End time of injection in seconds.
|
||||
/// @param[in] amount Amount to be injected per second.
|
||||
PolymerInflowBasic::PolymerInflowBasic(const double starttime,
|
||||
const double endtime,
|
||||
const double amount)
|
||||
: stime_(starttime), etime_(endtime), amount_(amount)
|
||||
{
|
||||
}
|
||||
|
||||
void PolymerInflowBasic::getInflowValues(const double step_start,
|
||||
const double step_end,
|
||||
std::vector<double>& poly_inflow_c) const
|
||||
{
|
||||
const double eps = 1e-5*(step_end - step_start);
|
||||
if (step_start + eps >= stime_ && step_end - eps <= etime_) {
|
||||
std::fill(poly_inflow_c.begin(), poly_inflow_c.end(), amount_);
|
||||
} else if (step_start + eps <= etime_ && step_end - eps >= stime_) {
|
||||
OPM_MESSAGE("Warning: polymer injection set to change inside timestep. Using value at start of step.");
|
||||
std::fill(poly_inflow_c.begin(), poly_inflow_c.end(), amount_);
|
||||
} else {
|
||||
std::fill(poly_inflow_c.begin(), poly_inflow_c.end(), 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------- Methods of PolymerInflowFromDeck ----------
|
||||
|
||||
|
||||
|
||||
/// Constructor.
|
||||
/// @param[in] deck Input deck expected to contain WPOLYMER.
|
||||
PolymerInflowFromDeck::PolymerInflowFromDeck(Opm::DeckConstPtr deck,
|
||||
const Wells& wells,
|
||||
const int num_cells)
|
||||
: sparse_inflow_(num_cells)
|
||||
{
|
||||
if (!deck->hasKeyword("WPOLYMER")) {
|
||||
OPM_MESSAGE("PolymerInflowFromDeck initialized without WPOLYMER in current epoch.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract concentrations and put into cell->concentration map.
|
||||
Opm::DeckKeywordConstPtr wpolymerKeyword = deck->getKeyword("WPOLYMER");
|
||||
const int num_wpl = wpolymerKeyword->size();
|
||||
std::map<int, double> perfcell_conc;
|
||||
for (int i = 0; i < num_wpl; ++i) {
|
||||
// Only use well name and polymer concentration.
|
||||
// That is, we ignore salt concentration and group
|
||||
// names.
|
||||
int wix = 0;
|
||||
for (; wix < wells.number_of_wells; ++wix) {
|
||||
if (wpolymerKeyword->getRecord(i)->getItem("WELL")->getString(0) == wells.name[wix]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (wix == wells.number_of_wells) {
|
||||
OPM_THROW(std::runtime_error, "Could not find a match for well "
|
||||
<< wpolymerKeyword->getRecord(i)->getItem("WELL")->getString(0)
|
||||
<< " from WPOLYMER.");
|
||||
}
|
||||
for (int j = wells.well_connpos[wix]; j < wells.well_connpos[wix+1]; ++j) {
|
||||
const int perf_cell = wells.well_cells[j];
|
||||
perfcell_conc[perf_cell] =
|
||||
wpolymerKeyword->getRecord(i)->getItem("POLYMER_CONCENTRATION")->getSIDouble(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Build sparse vector from map.
|
||||
std::map<int, double>::const_iterator it = perfcell_conc.begin();
|
||||
for (; it != perfcell_conc.end(); ++it) {
|
||||
sparse_inflow_.addElement(it->second, it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PolymerInflowFromDeck::setInflowValues(Opm::DeckConstPtr deck,
|
||||
Opm::EclipseStateConstPtr eclipseState,
|
||||
size_t currentStep)
|
||||
{
|
||||
Opm::DeckKeywordConstPtr keyword = deck->getKeyword("WPOLYMER");
|
||||
|
||||
// Schedule schedule(deck);
|
||||
ScheduleConstPtr schedule = eclipseState->getSchedule();
|
||||
for (size_t recordNr = 0; recordNr < keyword->size(); recordNr++) {
|
||||
DeckRecordConstPtr record = keyword->getRecord(recordNr);
|
||||
|
||||
const std::string& wellNamesPattern = record->getItem("WELL")->getTrimmedString(0);
|
||||
std::string wellName = record->getItem("WELL")->getTrimmedString(0);
|
||||
std::vector<WellPtr> wells = schedule->getWells(wellNamesPattern);
|
||||
for (auto wellIter = wells.begin(); wellIter != wells.end(); ++wellIter) {
|
||||
WellPtr well = *wellIter;
|
||||
WellInjectionProperties injection = well->getInjectionProperties(currentStep);
|
||||
if (injection.injectorType == WellInjector::WATER) {
|
||||
WellPolymerProperties polymer = well->getPolymerProperties(currentStep);
|
||||
wellPolymerRate_.insert(std::make_pair(wellName, polymer.m_polymerConcentration));
|
||||
} else {
|
||||
OPM_THROW(std::logic_error, "For polymer injector you must have a water injector");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor.
|
||||
/// @param[in] deck Input deck expected to contain WPOLYMER.
|
||||
PolymerInflowFromDeck::PolymerInflowFromDeck(Opm::DeckConstPtr deck,
|
||||
Opm::EclipseStateConstPtr eclipseState,
|
||||
const Wells& wells,
|
||||
const int num_cells,
|
||||
size_t currentStep)
|
||||
: sparse_inflow_(num_cells)
|
||||
{
|
||||
if (!deck->hasKeyword("WPOLYMER")) {
|
||||
OPM_MESSAGE("PolymerInflowFromDeck initialized without WPOLYMER in current epoch.");
|
||||
return;
|
||||
}
|
||||
setInflowValues(deck, eclipseState, currentStep);
|
||||
|
||||
std::unordered_map<std::string, double>::const_iterator map_it;
|
||||
// Extract concentrations and put into cell->concentration map.
|
||||
std::map<int, double> perfcell_conc;
|
||||
for (map_it = wellPolymerRate_.begin(); map_it != wellPolymerRate_.end(); ++map_it) {
|
||||
// Only use well name and polymer concentration.
|
||||
// That is, we ignore salt concentration and group
|
||||
// names.
|
||||
int wix = 0;
|
||||
for (; wix < wells.number_of_wells; ++wix) {
|
||||
if (wellPolymerRate_.count(wells.name[wix]) > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (wix == wells.number_of_wells) {
|
||||
OPM_THROW(std::runtime_error, "Could not find a match for well "
|
||||
<< map_it->first
|
||||
<< " from WPOLYMER.");
|
||||
|
||||
}
|
||||
for (int j = wells.well_connpos[wix]; j < wells.well_connpos[wix+1]; ++j) {
|
||||
const int perf_cell = wells.well_cells[j];
|
||||
perfcell_conc[perf_cell] = map_it->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Build sparse vector from map.
|
||||
std::map<int, double>::const_iterator it = perfcell_conc.begin();
|
||||
for (; it != perfcell_conc.end(); ++it) {
|
||||
sparse_inflow_.addElement(it->second, it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void PolymerInflowFromDeck::getInflowValues(const double /*step_start*/,
|
||||
const double /*step_end*/,
|
||||
std::vector<double>& poly_inflow_c) const
|
||||
{
|
||||
// This method does not depend on the given time,
|
||||
// instead one would have a new epoch (and create a new
|
||||
// instance) for each change in WPOLYMER.
|
||||
std::fill(poly_inflow_c.begin(), poly_inflow_c.end(), 0.0);
|
||||
const int nnz = sparse_inflow_.nonzeroSize();
|
||||
for (int i = 0; i < nnz; ++i) {
|
||||
poly_inflow_c[sparse_inflow_.nonzeroIndex(i)] = sparse_inflow_.nonzeroElement(i) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Opm
|
131
opm/polymer/PolymerInflow.hpp
Normal file
131
opm/polymer/PolymerInflow.hpp
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_POLYMERINFLOW_HEADER_INCLUDED
|
||||
#define OPM_POLYMERINFLOW_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/utility/SparseVector.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/WellPolymerProperties.hpp>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
struct Wells;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
/// @brief Interface for classes encapsulating polymer inflow information.
|
||||
class PolymerInflowInterface
|
||||
{
|
||||
public:
|
||||
/// Virtual destructor for subclassing.
|
||||
virtual ~PolymerInflowInterface() {}
|
||||
|
||||
/// Get inflow concentrations for all cells.
|
||||
/// \param[in] step_start Start of timestep.
|
||||
/// \param[in] step_end End of timestep.
|
||||
/// \param[out] poly_inflow_c Injection concentrations to use for timestep, per cell.
|
||||
/// Must be properly sized before calling.
|
||||
virtual void getInflowValues(const double step_start,
|
||||
const double step_end,
|
||||
std::vector<double>& poly_inflow_c) const = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// @brief Basic polymer injection behaviour class.
|
||||
/// This class gives all injectors the same polymer concentration,
|
||||
/// during a single time interval. Amount and interval can be specified.
|
||||
class PolymerInflowBasic : public PolymerInflowInterface
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \param[in] starttime Start time of injection in seconds.
|
||||
/// \param[in] endtime End time of injection in seconds.
|
||||
/// \param[in] amount Amount to be injected per second.
|
||||
PolymerInflowBasic(const double starttime,
|
||||
const double endtime,
|
||||
const double amount);
|
||||
|
||||
/// Get inflow concentrations for all cells.
|
||||
/// \param[in] step_start Start of timestep.
|
||||
/// \param[in] step_end End of timestep.
|
||||
/// \param[out] poly_inflow_c Injection concentrations to use for timestep, per cell.
|
||||
/// Must be properly sized before calling.
|
||||
virtual void getInflowValues(const double step_start,
|
||||
const double step_end,
|
||||
std::vector<double>& poly_inflow_c) const;
|
||||
private:
|
||||
double stime_;
|
||||
double etime_;
|
||||
double amount_;
|
||||
};
|
||||
|
||||
|
||||
/// @brief Polymer injection behaviour class using deck WPOLYMER.
|
||||
/// This class reads the accumulated WPOLYMER lines from the deck,
|
||||
/// and applies the last row given for each well.
|
||||
class PolymerInflowFromDeck : public PolymerInflowInterface
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \param[in] deck Input deck expected to contain WPOLYMER.
|
||||
/// \param[in] wells Wells structure.
|
||||
/// \param[in] num_cells Number of cells in grid.
|
||||
PolymerInflowFromDeck(Opm::DeckConstPtr deck,
|
||||
const Wells& wells,
|
||||
const int num_cells);
|
||||
|
||||
/// Constructor.
|
||||
/// \param[in] deck Input deck expected to contain WPOLYMER.
|
||||
/// \param[in] wells Wells structure.
|
||||
/// \param[in] num_cells Number of cells in grid.
|
||||
/// \param[in] currentStep Number of current simulation step.
|
||||
PolymerInflowFromDeck(Opm::DeckConstPtr deck,
|
||||
Opm::EclipseStateConstPtr eclipseState,
|
||||
const Wells& wells,
|
||||
const int num_cells,
|
||||
size_t currentStep);
|
||||
|
||||
/// Get inflow concentrations for all cells.
|
||||
/// \param[in] step_start Start of timestep.
|
||||
/// \param[in] step_end End of timestep.
|
||||
/// \param[out] poly_inflow_c Injection concentrations to use for timestep, per cell.
|
||||
/// Must be properly sized before calling.
|
||||
virtual void getInflowValues(const double /*step_start*/,
|
||||
const double /*step_end*/,
|
||||
std::vector<double>& poly_inflow_c) const;
|
||||
private:
|
||||
SparseVector<double> sparse_inflow_;
|
||||
|
||||
std::unordered_map<std::string, double> wellPolymerRate_;
|
||||
void setInflowValues(Opm::DeckConstPtr deck,
|
||||
Opm::EclipseStateConstPtr eclipseState,
|
||||
size_t currentStep);
|
||||
};
|
||||
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
#endif // OPM_POLYMERINFLOW_HEADER_INCLUDED
|
523
opm/polymer/PolymerProperties.cpp
Normal file
523
opm/polymer/PolymerProperties.cpp
Normal file
@ -0,0 +1,523 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
#include <opm/polymer/Point2D.hpp>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <opm/core/utility/linearInterpolation.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/common/Exceptions.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
double PolymerProperties::cMax() const
|
||||
{
|
||||
return c_max_;
|
||||
}
|
||||
|
||||
double PolymerProperties::mixParam() const
|
||||
{
|
||||
return mix_param_;
|
||||
}
|
||||
|
||||
double PolymerProperties::rockDensity() const
|
||||
{
|
||||
return rock_density_;
|
||||
}
|
||||
|
||||
double PolymerProperties::deadPoreVol() const
|
||||
{
|
||||
return dead_pore_vol_;
|
||||
}
|
||||
|
||||
double PolymerProperties::resFactor() const
|
||||
{
|
||||
return res_factor_;
|
||||
}
|
||||
|
||||
double PolymerProperties::cMaxAds() const
|
||||
{
|
||||
return c_max_ads_;
|
||||
}
|
||||
|
||||
int PolymerProperties::adsIndex() const
|
||||
{
|
||||
return ads_index_;
|
||||
}
|
||||
|
||||
const std::vector<double>&
|
||||
PolymerProperties::shearWaterVelocity() const
|
||||
{
|
||||
return water_vel_vals_;
|
||||
}
|
||||
|
||||
const std::vector<double>&
|
||||
PolymerProperties::shearViscosityReductionFactor() const
|
||||
{
|
||||
return shear_vrf_vals_;
|
||||
}
|
||||
|
||||
double PolymerProperties:: plyshlogRefConc() const
|
||||
{
|
||||
return plyshlog_ref_conc_;
|
||||
}
|
||||
|
||||
bool PolymerProperties::hasPlyshlogRefSalinity() const
|
||||
{
|
||||
return has_plyshlog_ref_salinity_;
|
||||
}
|
||||
|
||||
bool PolymerProperties::hasPlyshlogRefTemp() const
|
||||
{
|
||||
return has_plyshlog_ref_temp_;
|
||||
}
|
||||
|
||||
double PolymerProperties::plyshlogRefSalinity() const
|
||||
{
|
||||
return plyshlog_ref_salinity_;
|
||||
}
|
||||
|
||||
double PolymerProperties::plyshlogRefTemp() const
|
||||
{
|
||||
return plyshlog_ref_temp_;
|
||||
}
|
||||
|
||||
bool PolymerProperties::hasPlyshlog() const
|
||||
{
|
||||
return has_plyshlog_;
|
||||
}
|
||||
|
||||
bool PolymerProperties::hasShrate() const
|
||||
{
|
||||
return has_shrate_;
|
||||
}
|
||||
|
||||
|
||||
double PolymerProperties::shrate() const
|
||||
{
|
||||
return shrate_;
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
PolymerProperties::shearVrf(const double velocity) const
|
||||
{
|
||||
return Opm::linearInterpolation(water_vel_vals_, shear_vrf_vals_, velocity);
|
||||
}
|
||||
|
||||
double
|
||||
PolymerProperties::shearVrfWithDer(const double velocity, double& der) const
|
||||
{
|
||||
der = Opm::linearInterpolationDerivative(water_vel_vals_, shear_vrf_vals_, velocity);
|
||||
return Opm::linearInterpolation(water_vel_vals_, shear_vrf_vals_, velocity);
|
||||
}
|
||||
|
||||
double PolymerProperties::viscMult(double c) const
|
||||
{
|
||||
return Opm::linearInterpolation(c_vals_visc_, visc_mult_vals_, c);
|
||||
}
|
||||
|
||||
double PolymerProperties::viscMultWithDer(double c, double* der) const
|
||||
{
|
||||
*der = Opm::linearInterpolationDerivative(c_vals_visc_, visc_mult_vals_, c);
|
||||
return Opm::linearInterpolation(c_vals_visc_, visc_mult_vals_, c);
|
||||
}
|
||||
|
||||
void PolymerProperties::simpleAdsorption(double c, double& c_ads) const
|
||||
{
|
||||
double dummy;
|
||||
simpleAdsorptionBoth(c, c_ads, dummy, false);
|
||||
}
|
||||
|
||||
void PolymerProperties::simpleAdsorptionWithDer(double c, double& c_ads,
|
||||
double& dc_ads_dc) const
|
||||
{
|
||||
simpleAdsorptionBoth(c, c_ads, dc_ads_dc, true);
|
||||
}
|
||||
|
||||
void PolymerProperties::simpleAdsorptionBoth(double c, double& c_ads,
|
||||
double& dc_ads_dc, bool if_with_der) const
|
||||
{
|
||||
c_ads = Opm::linearInterpolation(c_vals_ads_, ads_vals_, c);;
|
||||
if (if_with_der) {
|
||||
dc_ads_dc = Opm::linearInterpolationDerivative(c_vals_ads_, ads_vals_, c);
|
||||
} else {
|
||||
dc_ads_dc = 0.;
|
||||
}
|
||||
}
|
||||
|
||||
void PolymerProperties::adsorption(double c, double cmax, double& c_ads) const
|
||||
{
|
||||
double dummy;
|
||||
adsorptionBoth(c, cmax, c_ads, dummy, false);
|
||||
}
|
||||
|
||||
void PolymerProperties::adsorptionWithDer(double c, double cmax,
|
||||
double& c_ads, double& dc_ads_dc) const
|
||||
{
|
||||
adsorptionBoth(c, cmax, c_ads, dc_ads_dc, true);
|
||||
}
|
||||
|
||||
void PolymerProperties::adsorptionBoth(double c, double cmax,
|
||||
double& c_ads, double& dc_ads_dc,
|
||||
bool if_with_der) const
|
||||
{
|
||||
if (ads_index_ == Desorption) {
|
||||
simpleAdsorptionBoth(c, c_ads, dc_ads_dc, if_with_der);
|
||||
} else if (ads_index_ == NoDesorption) {
|
||||
simpleAdsorptionBoth(std::max(c, cmax), c_ads, dc_ads_dc, if_with_der);
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, "Invalid Adsoption index");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PolymerProperties::effectiveVisc(const double c, const double* visc, double& mu_w_eff) const {
|
||||
effectiveInvVisc(c, visc, mu_w_eff);
|
||||
mu_w_eff = 1./mu_w_eff;
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveViscWithDer(const double c, const double* visc, double& mu_w_eff, double dmu_w_eff_dc) const {
|
||||
effectiveInvViscWithDer(c, visc, mu_w_eff, dmu_w_eff_dc);
|
||||
mu_w_eff = 1./mu_w_eff;
|
||||
dmu_w_eff_dc = -dmu_w_eff_dc*mu_w_eff*mu_w_eff;
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveInvVisc(const double c, const double* visc, double& inv_mu_w_eff) const
|
||||
{
|
||||
double dummy;
|
||||
effectiveInvViscBoth(c, visc, inv_mu_w_eff, dummy, false);
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveInvViscWithDer(const double c, const double* visc,
|
||||
double& inv_mu_w_eff,
|
||||
double& dinv_mu_w_eff_dc) const {
|
||||
effectiveInvViscBoth(c, visc, inv_mu_w_eff, dinv_mu_w_eff_dc, true);
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveInvViscBoth(const double c, const double* visc,
|
||||
double& inv_mu_w_eff,
|
||||
double& dinv_mu_w_eff_dc,
|
||||
bool if_with_der) const {
|
||||
double cbar = c/c_max_;
|
||||
double mu_w = visc[0];
|
||||
double mu_m;
|
||||
double omega = mix_param_;
|
||||
double dmu_m_dc;
|
||||
if (if_with_der) {
|
||||
mu_m = viscMultWithDer(c, &dmu_m_dc)*mu_w;
|
||||
dmu_m_dc *= mu_w;
|
||||
} else {
|
||||
mu_m = viscMult(c)*mu_w;
|
||||
}
|
||||
double mu_p = viscMult(c_max_)*mu_w;
|
||||
double inv_mu_m_omega = std::pow(mu_m, -omega);
|
||||
double inv_mu_w_e = inv_mu_m_omega*std::pow(mu_w, omega - 1.);
|
||||
double inv_mu_p_eff = inv_mu_m_omega*std::pow(mu_p, omega - 1.);
|
||||
inv_mu_w_eff = (1.0 - cbar)*inv_mu_w_e + cbar*inv_mu_p_eff;
|
||||
if (if_with_der) {
|
||||
double dinv_mu_w_e_dc = -omega*dmu_m_dc*std::pow(mu_m, -omega - 1)*std::pow(mu_w, omega - 1);
|
||||
double dinv_mu_p_eff_dc = -omega*dmu_m_dc*std::pow(mu_m, -omega - 1)*std::pow(mu_p, omega - 1);
|
||||
dinv_mu_w_eff_dc = (1 - cbar)*dinv_mu_w_e_dc + cbar*dinv_mu_p_eff_dc +
|
||||
1/c_max_*(inv_mu_p_eff - inv_mu_w_e);
|
||||
}
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveRelperm(const double c,
|
||||
const double cmax,
|
||||
const double* relperm,
|
||||
double& eff_relperm_wat) const {
|
||||
double dummy;
|
||||
effectiveRelpermBoth(c, cmax, relperm, 0, eff_relperm_wat,
|
||||
dummy, dummy, false);
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveRelpermWithDer (const double c,
|
||||
const double cmax,
|
||||
const double* relperm,
|
||||
const double* drelperm_ds,
|
||||
double& eff_relperm_wat,
|
||||
double& deff_relperm_wat_ds,
|
||||
double& deff_relperm_wat_dc) const {
|
||||
effectiveRelpermBoth(c, cmax, relperm,
|
||||
drelperm_ds, eff_relperm_wat,
|
||||
deff_relperm_wat_ds, deff_relperm_wat_dc,
|
||||
true);
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveRelpermBoth(const double c,
|
||||
const double cmax,
|
||||
const double* relperm,
|
||||
const double* drelperm_ds,
|
||||
double& eff_relperm_wat,
|
||||
double& deff_relperm_wat_ds,
|
||||
double& deff_relperm_wat_dc,
|
||||
bool if_with_der) const {
|
||||
double c_ads;
|
||||
double dc_ads_dc;
|
||||
adsorptionBoth(c, cmax, c_ads, dc_ads_dc, if_with_der);
|
||||
double rk = 1 + (res_factor_ - 1)*c_ads/c_max_ads_;
|
||||
eff_relperm_wat = relperm[0]/rk;
|
||||
if (if_with_der) {
|
||||
deff_relperm_wat_ds = (drelperm_ds[0]-drelperm_ds[2])/rk; //derivative with respect to sw
|
||||
//\frac{\partial k_{rw_eff}}{\parital c} = -\frac{krw}{rk^2}\frac{(RRF-1)}{c^a_{max}}\frac{\partial c^a}{\partial c}.
|
||||
deff_relperm_wat_dc = -(res_factor_ - 1)*dc_ads_dc*relperm[0]/(rk*rk*c_max_ads_);
|
||||
} else {
|
||||
deff_relperm_wat_ds = -1.0;
|
||||
deff_relperm_wat_dc = -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveMobilities(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
double* mob) const
|
||||
{
|
||||
double dummy;
|
||||
double dummy_pointer[4];
|
||||
effectiveMobilitiesBoth(c, cmax, visc, relperm,
|
||||
dummy_pointer, mob, dummy_pointer, dummy, false);
|
||||
}
|
||||
|
||||
|
||||
void PolymerProperties::effectiveMobilitiesWithDer(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
const double* drelpermds,
|
||||
double* mob,
|
||||
double* dmobds,
|
||||
double& dmobwatdc) const
|
||||
{
|
||||
effectiveMobilitiesBoth(c, cmax, visc,
|
||||
relperm, drelpermds, mob, dmobds,
|
||||
dmobwatdc, true);
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveMobilitiesBoth(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
const double* drelperm_ds,
|
||||
double* mob,
|
||||
double* dmob_ds,
|
||||
double& dmobwat_dc,
|
||||
bool if_with_der) const
|
||||
{
|
||||
double inv_mu_w_eff;
|
||||
double dinv_mu_w_eff_dc;
|
||||
effectiveInvViscBoth(c, visc, inv_mu_w_eff, dinv_mu_w_eff_dc, if_with_der);
|
||||
double eff_relperm_wat;
|
||||
double deff_relperm_wat_ds;
|
||||
double deff_relperm_wat_dc;
|
||||
|
||||
effectiveRelpermBoth(c, cmax, relperm,
|
||||
drelperm_ds, eff_relperm_wat,
|
||||
deff_relperm_wat_ds, deff_relperm_wat_dc,
|
||||
if_with_der);
|
||||
|
||||
// The "function" eff_relperm_wat is defined as a function of only sw (so that its
|
||||
// partial derivative with respect to so is zero).
|
||||
|
||||
mob[0] = eff_relperm_wat*inv_mu_w_eff;
|
||||
mob[1] = relperm[1]/visc[1];
|
||||
|
||||
if (if_with_der) {
|
||||
dmobwat_dc = eff_relperm_wat*dinv_mu_w_eff_dc
|
||||
+ deff_relperm_wat_dc*inv_mu_w_eff;
|
||||
dmob_ds[0*2 + 0] = deff_relperm_wat_ds*inv_mu_w_eff;
|
||||
// one have to deside which variables to derive
|
||||
// here the full derivative is written out
|
||||
dmob_ds[0*2 + 1] = 0.0*(drelperm_ds[0*2 + 1] - drelperm_ds[1*2 + 1])/visc[1];
|
||||
dmob_ds[1*2 + 0] = -0.0*deff_relperm_wat_ds*inv_mu_w_eff;
|
||||
dmob_ds[1*2 + 1] = (drelperm_ds[1*2 + 1] - drelperm_ds[0*2 + 1])/visc[1];
|
||||
}
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveTotalMobility(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
double& totmob) const
|
||||
{
|
||||
double dummy1[4];
|
||||
double dummy2[2];
|
||||
effectiveTotalMobilityBoth(c, cmax, visc, relperm, dummy1,
|
||||
totmob, dummy2, false);
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveTotalMobilityWithDer(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
const double* drelperm_ds,
|
||||
double& totmob,
|
||||
double* dtotmob_dsdc) const
|
||||
{
|
||||
effectiveTotalMobilityBoth(c, cmax, visc, relperm, drelperm_ds,
|
||||
totmob, dtotmob_dsdc, true);
|
||||
}
|
||||
|
||||
void PolymerProperties::effectiveTotalMobilityBoth(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
const double* drelperm_ds,
|
||||
double& totmob,
|
||||
double* dtotmob_dsdc,
|
||||
bool if_with_der) const
|
||||
{
|
||||
double mob[2];
|
||||
double dmob_ds[4];
|
||||
double dmobwat_dc;
|
||||
effectiveMobilitiesBoth(c, cmax, visc, relperm, drelperm_ds,
|
||||
mob, dmob_ds, dmobwat_dc, if_with_der);
|
||||
totmob = mob[0] + mob[1];
|
||||
if (if_with_der) {
|
||||
dtotmob_dsdc[0] = dmob_ds[0*2 + 0] - dmob_ds[1*2 + 0]
|
||||
+ dmob_ds[0*2 + 1] - dmob_ds[1*2 + 1]; //derivative with respect to sw
|
||||
dtotmob_dsdc[1] = dmobwat_dc; //derivative with respect to c
|
||||
}
|
||||
}
|
||||
|
||||
void PolymerProperties::computeMc(const double& c, double& mc) const
|
||||
{
|
||||
double dummy;
|
||||
computeMcBoth(c, mc, dummy, false);
|
||||
}
|
||||
|
||||
void PolymerProperties::computeMcWithDer(const double& c, double& mc,
|
||||
double& dmc_dc) const
|
||||
{
|
||||
computeMcBoth(c, mc, dmc_dc, true);
|
||||
}
|
||||
|
||||
void PolymerProperties::computeMcBoth(const double& c, double& mc,
|
||||
double& dmc_dc, bool if_with_der) const
|
||||
{
|
||||
double cbar = c/c_max_;
|
||||
double omega = mix_param_;
|
||||
double r = std::pow(viscMult(c_max_), 1 - omega); // viscMult(c_max_)=mu_p/mu_w
|
||||
mc = c/(cbar + (1 - cbar)*r);
|
||||
if (if_with_der) {
|
||||
dmc_dc = r/std::pow(cbar + (1 - cbar)*r, 2);
|
||||
} else {
|
||||
dmc_dc = 0.;
|
||||
}
|
||||
}
|
||||
|
||||
bool PolymerProperties::computeShearMultLog(std::vector<double>& water_vel, std::vector<double>& visc_mult, std::vector<double>& shear_mult) const
|
||||
{
|
||||
|
||||
double refConcentration = plyshlogRefConc();
|
||||
double refViscMult = viscMult(refConcentration);
|
||||
|
||||
std::vector<double> shear_water_vel = shearWaterVelocity();
|
||||
std::vector<double> shear_vrf = shearViscosityReductionFactor();
|
||||
|
||||
std::vector<double> logShearWaterVel;
|
||||
std::vector<double> logShearVRF;
|
||||
|
||||
logShearWaterVel.resize(shear_water_vel.size());
|
||||
logShearVRF.resize(shear_water_vel.size());
|
||||
|
||||
// converting the table using the reference condition
|
||||
for (size_t i = 0; i < shear_vrf.size(); ++i) {
|
||||
shear_vrf[i] = (refViscMult * shear_vrf[i] - 1.) / (refViscMult - 1);
|
||||
logShearWaterVel[i] = std::log(shear_water_vel[i]);
|
||||
}
|
||||
|
||||
shear_mult.resize(water_vel.size());
|
||||
|
||||
// the mimum velocity to apply the shear-thinning
|
||||
const double minShearVel = shear_water_vel[0];
|
||||
const double maxShearVel = shear_water_vel.back();
|
||||
const double epsilon = std::sqrt(std::numeric_limits<double>::epsilon());
|
||||
|
||||
for (size_t i = 0; i < water_vel.size(); ++i) {
|
||||
|
||||
if (visc_mult[i] - 1. < epsilon || std::abs(water_vel[i]) < minShearVel) {
|
||||
shear_mult[i] = 1.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < shear_vrf.size(); ++j) {
|
||||
logShearVRF[j] = (1 + (visc_mult[i] - 1.0) * shear_vrf[j]) / visc_mult[i];
|
||||
logShearVRF[j] = std::log(logShearVRF[j]);
|
||||
}
|
||||
|
||||
// const double logWaterVelO = std::log(water_vel[i]);
|
||||
const double logWaterVelO = std::log(std::abs(water_vel[i]));
|
||||
|
||||
size_t iIntersection; // finding the intersection on the iIntersectionth table segment
|
||||
bool foundSegment = false;
|
||||
|
||||
for (iIntersection = 0; iIntersection < shear_vrf.size() - 1; ++iIntersection) {
|
||||
|
||||
double temp1 = logShearVRF[iIntersection] + logShearWaterVel[iIntersection] - logWaterVelO;
|
||||
double temp2 = logShearVRF[iIntersection + 1] + logShearWaterVel[iIntersection + 1] - logWaterVelO;
|
||||
|
||||
// ignore the cases the temp1 or temp2 is zero first for simplicity.
|
||||
// several more complicated cases remain to be implemented.
|
||||
if( temp1 * temp2 < 0.){
|
||||
foundSegment = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundSegment == true) {
|
||||
detail::Point2D lineSegment[2];
|
||||
lineSegment[0] = detail::Point2D{logShearWaterVel[iIntersection], logShearVRF[iIntersection]};
|
||||
lineSegment[1] = detail::Point2D{logShearWaterVel[iIntersection + 1], logShearVRF[iIntersection + 1]};
|
||||
|
||||
detail::Point2D line[2];
|
||||
line[0] = detail::Point2D{0, logWaterVelO};
|
||||
line[1] = detail::Point2D{logWaterVelO, 0};
|
||||
|
||||
detail::Point2D intersectionPoint;
|
||||
|
||||
bool foundIntersection = detail::Point2D::findIntersection(lineSegment, line, intersectionPoint);
|
||||
|
||||
if (foundIntersection) {
|
||||
shear_mult[i] = std::exp(intersectionPoint.getY());
|
||||
} else {
|
||||
std::cerr << " failed in finding the solution for shear-thinning multiplier " << std::endl;
|
||||
return false; // failed in finding the solution.
|
||||
}
|
||||
} else {
|
||||
// check if the failure in finding the shear multiplier is due to too big water velocity.
|
||||
if ((logWaterVelO - logShearVRF.back()) < logShearWaterVel.back()) {
|
||||
std::cout << " the veclocity is " << water_vel[i] << std::endl;
|
||||
std::cout << " max shear velocity is " << maxShearVel << std::endl;
|
||||
std::cerr << " something wrong happend in finding segment" << std::endl;
|
||||
return false;
|
||||
} else {
|
||||
shear_mult[i] = std::exp(logShearVRF.back());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
408
opm/polymer/PolymerProperties.hpp
Normal file
408
opm/polymer/PolymerProperties.hpp
Normal file
@ -0,0 +1,408 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_POLYMERPROPERTIES_HEADER_INCLUDED
|
||||
#define OPM_POLYMERPROPERTIES_HEADER_INCLUDED
|
||||
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class PolymerProperties
|
||||
{
|
||||
public:
|
||||
PolymerProperties()
|
||||
{
|
||||
}
|
||||
|
||||
enum AdsorptionBehaviour { Desorption = 1, NoDesorption = 2 };
|
||||
|
||||
/// Construct from parameters
|
||||
/// \param[in] c_max Maximum polymer concentration used in computation of effective viscosity
|
||||
/// \param[in] mix_param Mixing parameter
|
||||
/// \param[in] rock_density Rock density
|
||||
/// \param[in] dead_pore_vol Dead pore volume
|
||||
/// \param[in] res_factor Residual resistance factor
|
||||
/// \param[in] c_max_ads Maximum polymer adsorption value used in computation of the resistance factor
|
||||
/// \param[in] c_vals_visc Array of concentration for effective vicosity multiplier
|
||||
/// \param[in] visc_mult_vals Array of effective vicosity multiplier
|
||||
/// \param[in] c_vals_ads Array of concentration for adsorption values
|
||||
/// \param[in] ads_vals Array of adsorption values
|
||||
/// \param[in] water_vel_vals_ Array of water phase velocity for shear
|
||||
/// \param[in] shear_vrf_vals_ Array of viscosity reduction factor
|
||||
PolymerProperties(double c_max,
|
||||
double mix_param,
|
||||
double rock_density,
|
||||
double dead_pore_vol,
|
||||
double res_factor,
|
||||
double c_max_ads,
|
||||
AdsorptionBehaviour ads_index,
|
||||
const std::vector<double>& c_vals_visc,
|
||||
const std::vector<double>& visc_mult_vals,
|
||||
const std::vector<double>& c_vals_ads,
|
||||
const std::vector<double>& ads_vals,
|
||||
const std::vector<double>& water_vel_vals,
|
||||
const std::vector<double>& shear_vrf_vals
|
||||
)
|
||||
: c_max_(c_max),
|
||||
mix_param_(mix_param),
|
||||
rock_density_(rock_density),
|
||||
dead_pore_vol_(dead_pore_vol),
|
||||
res_factor_(res_factor),
|
||||
c_max_ads_(c_max_ads),
|
||||
ads_index_(ads_index),
|
||||
c_vals_visc_(c_vals_visc),
|
||||
visc_mult_vals_(visc_mult_vals),
|
||||
c_vals_ads_(c_vals_ads),
|
||||
ads_vals_(ads_vals),
|
||||
water_vel_vals_(water_vel_vals),
|
||||
shear_vrf_vals_(shear_vrf_vals)
|
||||
{
|
||||
}
|
||||
|
||||
PolymerProperties(Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclipseState)
|
||||
{
|
||||
readFromDeck(deck, eclipseState);
|
||||
}
|
||||
|
||||
void set(double c_max,
|
||||
double mix_param,
|
||||
double rock_density,
|
||||
double dead_pore_vol,
|
||||
double res_factor,
|
||||
double c_max_ads,
|
||||
AdsorptionBehaviour ads_index,
|
||||
const std::vector<double>& c_vals_visc,
|
||||
const std::vector<double>& visc_mult_vals,
|
||||
const std::vector<double>& c_vals_ads,
|
||||
const std::vector<double>& ads_vals,
|
||||
const std::vector<double>& water_vel_vals,
|
||||
const std::vector<double>& shear_vrf_vals
|
||||
)
|
||||
{
|
||||
c_max_ = c_max;
|
||||
mix_param_ = mix_param;
|
||||
rock_density_ = rock_density;
|
||||
dead_pore_vol_ = dead_pore_vol;
|
||||
res_factor_ = res_factor;
|
||||
c_max_ads_ = c_max_ads;
|
||||
c_vals_visc_ = c_vals_visc;
|
||||
visc_mult_vals_ = visc_mult_vals;
|
||||
c_vals_ads_ = c_vals_ads;
|
||||
ads_vals_ = ads_vals;
|
||||
ads_index_ = ads_index;
|
||||
water_vel_vals_ = water_vel_vals;
|
||||
shear_vrf_vals_ = shear_vrf_vals;
|
||||
}
|
||||
|
||||
void readFromDeck(Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclipseState)
|
||||
{
|
||||
// We assume NTMISC=1
|
||||
auto tables = eclipseState->getTableManager();
|
||||
const auto& plymaxTable = tables->getPlymaxTables().getTable<PlymaxTable>(0);
|
||||
const auto plmixparRecord = deck->getKeyword("PLMIXPAR")->getRecord(0);
|
||||
|
||||
// We also assume that each table has exactly one row...
|
||||
assert(plymaxTable.numRows() == 1);
|
||||
|
||||
c_max_ = plymaxTable.getPolymerConcentrationColumn()[0];
|
||||
mix_param_ = plmixparRecord->getItem("TODD_LONGSTAFF")->getSIDouble(0);
|
||||
|
||||
// We assume NTSFUN=1
|
||||
const auto& plyrockTable = tables->getPlyrockTables().getTable<PlyrockTable>(0);
|
||||
|
||||
// We also assume that each table has exactly one row...
|
||||
assert(plyrockTable.numRows() == 1);
|
||||
|
||||
dead_pore_vol_ = plyrockTable.getDeadPoreVolumeColumn()[0];
|
||||
res_factor_ = plyrockTable.getResidualResistanceFactorColumn()[0];
|
||||
rock_density_ = plyrockTable.getRockDensityFactorColumn()[0];
|
||||
ads_index_ = static_cast<AdsorptionBehaviour>(plyrockTable.getAdsorbtionIndexColumn()[0]);
|
||||
c_max_ads_ = plyrockTable.getMaxAdsorbtionColumn()[0];
|
||||
|
||||
// We assume NTPVT=1
|
||||
const auto& plyviscTable = tables->getPlyviscTables().getTable<PlyviscTable>(0);
|
||||
|
||||
|
||||
c_vals_visc_ = plyviscTable.getPolymerConcentrationColumn();
|
||||
visc_mult_vals_ = plyviscTable.getViscosityMultiplierColumn();
|
||||
|
||||
// We assume NTSFUN=1
|
||||
const auto& plyadsTable = tables->getPlyadsTables().getTable<PlyadsTable>(0);
|
||||
|
||||
c_vals_ads_ = plyadsTable.getPolymerConcentrationColumn();
|
||||
ads_vals_ = plyadsTable.getAdsorbedPolymerColumn();
|
||||
|
||||
has_plyshlog_ = deck->hasKeyword("PLYSHLOG");
|
||||
has_shrate_ = deck->hasKeyword("SHRATE");
|
||||
|
||||
if (has_plyshlog_) {
|
||||
// Assuming NTPVT == 1 always
|
||||
const auto& plyshlogTable = tables->getPlyshlogTables().getTable<PlyshlogTable>(0);
|
||||
|
||||
water_vel_vals_ = plyshlogTable.getWaterVelocityColumn();
|
||||
shear_vrf_vals_ = plyshlogTable.getShearMultiplierColumn();
|
||||
|
||||
// do the unit version here for the water_vel_vals_
|
||||
Opm::UnitSystem unitSystem = *deck->getActiveUnitSystem();
|
||||
double siFactor;
|
||||
if (has_shrate_) {
|
||||
siFactor = unitSystem.parse("1/Time")->getSIScaling();
|
||||
DeckKeywordConstPtr shrateKeyword = deck->getKeyword("SHRATE");
|
||||
std::vector<double> shrate_readin = shrateKeyword->getSIDoubleData();
|
||||
if (shrate_readin.size() == 1) {
|
||||
shrate_ = shrate_readin[0];
|
||||
} else if (shrate_readin.size() == 0) {
|
||||
shrate_ = 4.8; // default value
|
||||
} else {
|
||||
OPM_THROW(std::logic_error, "Only NTPVT == 1 is allowed for SHRATE keyword now !\n");
|
||||
}
|
||||
} else {
|
||||
siFactor = unitSystem.parse("Length/Time")->getSIScaling();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < water_vel_vals_.size(); ++i) {
|
||||
water_vel_vals_[i] *= siFactor;
|
||||
}
|
||||
|
||||
|
||||
plyshlog_ref_conc_ = plyshlogTable.getRefPolymerConcentration();
|
||||
|
||||
if (plyshlogTable.hasRefSalinity()) {
|
||||
has_plyshlog_ref_salinity_ = true;
|
||||
plyshlog_ref_salinity_ = plyshlogTable.getRefSalinity();
|
||||
} else {
|
||||
has_plyshlog_ref_salinity_ = false;
|
||||
}
|
||||
|
||||
if (plyshlogTable.hasRefTemperature()) {
|
||||
has_plyshlog_ref_temp_ = true;
|
||||
plyshlog_ref_temp_ = plyshlogTable.getRefTemperature();
|
||||
} else {
|
||||
has_plyshlog_ref_temp_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double cMax() const;
|
||||
|
||||
double mixParam() const;
|
||||
|
||||
double rockDensity() const;
|
||||
|
||||
double deadPoreVol() const;
|
||||
|
||||
double resFactor() const;
|
||||
|
||||
double cMaxAds() const;
|
||||
|
||||
int adsIndex() const;
|
||||
|
||||
/// indicate whehter PLYSHLOG is specified
|
||||
bool hasPlyshlog() const;
|
||||
|
||||
/// the water velocity or water shear rate in PLYSHLOG table
|
||||
const std::vector<double>& shearWaterVelocity() const;
|
||||
|
||||
/// the viscosity reduction factor PLYSHLOG table
|
||||
const std::vector<double>& shearViscosityReductionFactor() const;
|
||||
|
||||
/// the reference polymer concentration in PLYSHLOG
|
||||
double plyshlogRefConc() const;
|
||||
|
||||
/// indicate wheter reference salinity is specified in PLYSHLOG
|
||||
bool hasPlyshlogRefSalinity() const;
|
||||
|
||||
/// indicate whether reference temperature is specified in PLYSHLOG
|
||||
bool hasPlyshlogRefTemp() const;
|
||||
|
||||
/// the reference salinity in PLYSHLOG
|
||||
double plyshlogRefSalinity() const;
|
||||
|
||||
/// the reference temperature in PLYSHLOG
|
||||
double plyshlogRefTemp() const;
|
||||
|
||||
/// indicate whether SHRATE keyword is specified
|
||||
bool hasShrate() const;
|
||||
|
||||
/// the value of SHRATE
|
||||
double shrate() const;
|
||||
|
||||
double shearVrf(const double velocity) const;
|
||||
|
||||
double shearVrfWithDer(const double velocity, double& der) const;
|
||||
|
||||
double viscMult(double c) const;
|
||||
|
||||
double viscMultWithDer(double c, double* der) const;
|
||||
|
||||
void simpleAdsorption(double c, double& c_ads) const;
|
||||
|
||||
void simpleAdsorptionWithDer(double c, double& c_ads,
|
||||
double& dc_ads_dc) const;
|
||||
|
||||
void adsorption(double c, double cmax, double& c_ads) const;
|
||||
|
||||
void adsorptionWithDer(double c, double cmax,
|
||||
double& c_ads, double& dc_ads_dc) const;
|
||||
|
||||
void effectiveVisc(const double c, const double* visc,
|
||||
double& mu_w_eff) const;
|
||||
|
||||
void effectiveViscWithDer(const double c, const double* visc
|
||||
, double& mu_w_eff
|
||||
, double dmu_w_eff_dc) const;
|
||||
|
||||
void effectiveInvVisc(const double c, const double* visc,
|
||||
double& inv_mu_w_eff) const;
|
||||
|
||||
void effectiveInvViscWithDer(const double c,
|
||||
const double* visc,
|
||||
double& inv_mu_w_eff,
|
||||
double& dinv_mu_w_eff_dc) const;
|
||||
void effectiveRelperm(const double c,
|
||||
const double cmax,
|
||||
const double* relperm,
|
||||
double& eff_relperm_wat) const;
|
||||
|
||||
void effectiveRelpermWithDer (const double c,
|
||||
const double cmax,
|
||||
const double* relperm,
|
||||
const double* drelperm_ds,
|
||||
double& eff_relperm_wat,
|
||||
double& deff_relperm_wat_ds,
|
||||
double& deff_relperm_wat_dc) const;
|
||||
|
||||
void effectiveMobilities(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
double* mob) const;
|
||||
|
||||
void effectiveMobilitiesWithDer(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
const double* drelpermds,
|
||||
double* mob,
|
||||
double* dmob_ds,
|
||||
double& dmobwatdc) const;
|
||||
|
||||
void effectiveMobilitiesBoth(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
const double* drelperm_ds,
|
||||
double* mob,
|
||||
double* dmob_ds,
|
||||
double& dmobwat_dc,
|
||||
bool if_with_der) const;
|
||||
|
||||
void effectiveTotalMobility(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
double& totmob) const;
|
||||
|
||||
void effectiveTotalMobilityWithDer(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
const double* drelpermds,
|
||||
double& totmob,
|
||||
double* dtotmob_dsdc) const;
|
||||
|
||||
void effectiveTotalMobilityBoth(const double c,
|
||||
const double cmax,
|
||||
const double* visc,
|
||||
const double* relperm,
|
||||
const double* drelperm_ds,
|
||||
double& totmob,
|
||||
double* dtotmob_dsdc,
|
||||
bool if_with_der) const;
|
||||
|
||||
void computeMc(const double& c, double& mc) const;
|
||||
|
||||
void computeMcWithDer(const double& c, double& mc,
|
||||
double& dmc_dc) const;
|
||||
|
||||
void computeMcBoth(const double& c, double& mc,
|
||||
double& dmc_dc, bool if_with_der) const;
|
||||
|
||||
/// Computing the shear multiplier based on the water velocity/shear rate with PLYSHLOG keyword
|
||||
bool computeShearMultLog(std::vector<double>& water_vel, std::vector<double>& visc_mult, std::vector<double>& shear_mult) const;
|
||||
|
||||
private:
|
||||
double c_max_;
|
||||
double mix_param_;
|
||||
double rock_density_;
|
||||
double dead_pore_vol_;
|
||||
double res_factor_;
|
||||
double c_max_ads_;
|
||||
|
||||
bool has_plyshlog_;
|
||||
bool has_shrate_;
|
||||
// Assuming NTPVT == 1 always due to the limitation of the parser
|
||||
// only one SHRATE value
|
||||
// TODO: to be extended later when parser is improved.
|
||||
double shrate_;
|
||||
AdsorptionBehaviour ads_index_;
|
||||
std::vector<double> c_vals_visc_;
|
||||
std::vector<double> visc_mult_vals_;
|
||||
std::vector<double> c_vals_ads_;
|
||||
std::vector<double> ads_vals_;
|
||||
std::vector<double> water_vel_vals_;
|
||||
std::vector<double> shear_vrf_vals_;
|
||||
|
||||
double plyshlog_ref_conc_;
|
||||
double plyshlog_ref_salinity_;
|
||||
double plyshlog_ref_temp_;
|
||||
bool has_plyshlog_ref_salinity_;
|
||||
bool has_plyshlog_ref_temp_;
|
||||
|
||||
|
||||
void simpleAdsorptionBoth(double c, double& c_ads,
|
||||
double& dc_ads_dc, bool if_with_der) const;
|
||||
void adsorptionBoth(double c, double cmax,
|
||||
double& c_ads, double& dc_ads_dc,
|
||||
bool if_with_der) const;
|
||||
void effectiveInvViscBoth(const double c, const double* visc,
|
||||
double& inv_mu_w_eff,
|
||||
double& dinv_mu_w_eff_dc, bool if_with_der) const;
|
||||
void effectiveRelpermBoth(const double c,
|
||||
const double cmax,
|
||||
const double* relperm,
|
||||
const double* drelperm_ds,
|
||||
double& eff_relperm_wat,
|
||||
double& deff_relperm_wat_ds,
|
||||
double& deff_relperm_wat_dc,
|
||||
bool if_with_der) const;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_POLYMERPROPERTIES_HEADER_INCLUDED
|
90
opm/polymer/PolymerState.hpp
Normal file
90
opm/polymer/PolymerState.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_POLYMERSTATE_HEADER_INCLUDED
|
||||
#define OPM_POLYMERSTATE_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/core/simulator/TwophaseState.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// Simulator state for a two-phase simulator with polymer.
|
||||
class PolymerState
|
||||
{
|
||||
public:
|
||||
void init(const UnstructuredGrid& g, int num_phases)
|
||||
{
|
||||
this->init(g.number_of_cells, g.number_of_faces, num_phases);
|
||||
}
|
||||
|
||||
void init(int number_of_cells, int number_of_faces, int num_phases)
|
||||
{
|
||||
state2p_.init(number_of_cells, number_of_faces, num_phases);
|
||||
concentration_.resize(number_of_cells, 0.0);
|
||||
cmax_.resize(number_of_cells, 0.0);
|
||||
}
|
||||
|
||||
enum ExtremalSat { MinSat = TwophaseState::MinSat, MaxSat = TwophaseState::MaxSat };
|
||||
|
||||
void setFirstSat(const std::vector<int>& cells,
|
||||
const Opm::IncompPropertiesInterface& props,
|
||||
ExtremalSat es)
|
||||
{
|
||||
// A better solution for embedding TwophaseState::ExtremalSat could perhaps
|
||||
// be found, to avoid the cast.
|
||||
state2p_.setFirstSat(cells, props, static_cast<TwophaseState::ExtremalSat>(es));
|
||||
}
|
||||
|
||||
inline int numPhases() const
|
||||
{
|
||||
return state2p_.numPhases();
|
||||
}
|
||||
std::vector<double>& pressure () { return state2p_.pressure(); }
|
||||
std::vector<double>& facepressure() { return state2p_.facepressure(); }
|
||||
std::vector<double>& faceflux () { return state2p_.faceflux(); }
|
||||
std::vector<double>& saturation () { return state2p_.saturation(); }
|
||||
std::vector<double>& concentration() { return concentration_; }
|
||||
std::vector<double>& maxconcentration() { return cmax_; }
|
||||
|
||||
const std::vector<double>& pressure () const { return state2p_.pressure(); }
|
||||
const std::vector<double>& facepressure() const { return state2p_.facepressure(); }
|
||||
const std::vector<double>& faceflux () const { return state2p_.faceflux(); }
|
||||
const std::vector<double>& saturation () const { return state2p_.saturation(); }
|
||||
const std::vector<double>& concentration() const { return concentration_; }
|
||||
const std::vector<double>& maxconcentration() const { return cmax_; }
|
||||
|
||||
TwophaseState& twophaseState() { return state2p_; }
|
||||
const TwophaseState& twophaseState() const { return state2p_; }
|
||||
|
||||
private:
|
||||
TwophaseState state2p_;
|
||||
std::vector<double> concentration_;
|
||||
std::vector<double> cmax_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // OPM_POLYMERSTATE_HEADER_INCLUDED
|
620
opm/polymer/SimulatorCompressiblePolymer.cpp
Normal file
620
opm/polymer/SimulatorCompressiblePolymer.cpp
Normal file
@ -0,0 +1,620 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/polymer/SimulatorCompressiblePolymer.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
|
||||
#include <opm/polymer/CompressibleTpfaPolymer.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/pressure/flow_bc.h>
|
||||
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/StopWatch.hpp>
|
||||
#include <opm/core/io/vtk/writeVtkData.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/miscUtilitiesBlackoil.hpp>
|
||||
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
|
||||
#include <opm/core/props/BlackoilPropertiesInterface.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/core/grid/ColumnExtract.hpp>
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <opm/polymer/PolymerBlackoilState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/polymer/TransportSolverTwophaseCompressiblePolymer.hpp>
|
||||
#include <opm/polymer/PolymerInflow.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
#include <opm/polymer/polymerUtilities.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
void outputStateVtk(const UnstructuredGrid& grid,
|
||||
const Opm::PolymerBlackoilState& state,
|
||||
const int step,
|
||||
const std::string& output_dir);
|
||||
void outputStateMatlab(const UnstructuredGrid& grid,
|
||||
const Opm::PolymerBlackoilState& state,
|
||||
const int step,
|
||||
const std::string& output_dir);
|
||||
void outputWaterCut(const Opm::Watercut& watercut,
|
||||
const std::string& output_dir);
|
||||
void outputWellReport(const Opm::WellReport& wellreport,
|
||||
const std::string& output_dir);
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
||||
class SimulatorCompressiblePolymer::Impl
|
||||
{
|
||||
public:
|
||||
Impl(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const PolymerProperties& poly_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const PolymerInflowInterface& polymer_inflow,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double* gravity);
|
||||
|
||||
SimulatorReport run(SimulatorTimer& timer,
|
||||
PolymerBlackoilState& state,
|
||||
WellState& well_state);
|
||||
|
||||
private:
|
||||
// Data.
|
||||
|
||||
// Parameters for output.
|
||||
bool output_;
|
||||
bool output_vtk_;
|
||||
std::string output_dir_;
|
||||
int output_interval_;
|
||||
// Parameters for well control
|
||||
bool check_well_controls_;
|
||||
int max_well_control_iterations_;
|
||||
// Parameters for transport solver.
|
||||
int num_transport_substeps_;
|
||||
bool use_segregation_split_;
|
||||
// Observed objects.
|
||||
const UnstructuredGrid& grid_;
|
||||
const BlackoilPropertiesInterface& props_;
|
||||
const PolymerProperties& poly_props_;
|
||||
const RockCompressibility* rock_comp_props_;
|
||||
WellsManager& wells_manager_;
|
||||
const Wells* wells_;
|
||||
const PolymerInflowInterface& polymer_inflow_;
|
||||
const double* gravity_;
|
||||
// Solvers
|
||||
CompressibleTpfaPolymer psolver_;
|
||||
TransportSolverTwophaseCompressiblePolymer tsolver_;
|
||||
// Needed by column-based gravity segregation solver.
|
||||
std::vector< std::vector<int> > columns_;
|
||||
// Misc. data
|
||||
std::vector<int> allcells_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
SimulatorCompressiblePolymer::SimulatorCompressiblePolymer(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const PolymerProperties& poly_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const PolymerInflowInterface& polymer_inflow,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double* gravity)
|
||||
{
|
||||
pimpl_.reset(new Impl(param, grid, props, poly_props, rock_comp_props,
|
||||
wells_manager, polymer_inflow, linsolver, gravity));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
SimulatorReport SimulatorCompressiblePolymer::run(SimulatorTimer& timer,
|
||||
PolymerBlackoilState& state,
|
||||
WellState& well_state)
|
||||
{
|
||||
return pimpl_->run(timer, state, well_state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SimulatorCompressiblePolymer::Impl::Impl(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const PolymerProperties& poly_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const PolymerInflowInterface& polymer_inflow,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double* gravity)
|
||||
: grid_(grid),
|
||||
props_(props),
|
||||
poly_props_(poly_props),
|
||||
rock_comp_props_(rock_comp_props),
|
||||
wells_manager_(wells_manager),
|
||||
wells_(wells_manager.c_wells()),
|
||||
polymer_inflow_(polymer_inflow),
|
||||
gravity_(gravity),
|
||||
psolver_(grid, props, rock_comp_props, poly_props, linsolver,
|
||||
param.getDefault("nl_pressure_residual_tolerance", 0.0),
|
||||
param.getDefault("nl_pressure_change_tolerance", 1.0),
|
||||
param.getDefault("nl_pressure_maxiter", 10),
|
||||
gravity, wells_manager.c_wells()),
|
||||
tsolver_(grid, props, poly_props,
|
||||
TransportSolverTwophaseCompressiblePolymer::Bracketing,
|
||||
param.getDefault("nl_tolerance", 1e-9),
|
||||
param.getDefault("nl_maxiter", 30))
|
||||
{
|
||||
// For output.
|
||||
output_ = param.getDefault("output", true);
|
||||
if (output_) {
|
||||
output_vtk_ = param.getDefault("output_vtk", true);
|
||||
output_dir_ = param.getDefault("output_dir", std::string("output"));
|
||||
// Ensure that output dir exists
|
||||
boost::filesystem::path fpath(output_dir_);
|
||||
try {
|
||||
create_directories(fpath);
|
||||
}
|
||||
catch (...) {
|
||||
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
||||
}
|
||||
output_interval_ = param.getDefault("output_interval", 1);
|
||||
}
|
||||
|
||||
// Well control related init.
|
||||
check_well_controls_ = param.getDefault("check_well_controls", false);
|
||||
max_well_control_iterations_ = param.getDefault("max_well_control_iterations", 10);
|
||||
|
||||
// Transport related init.
|
||||
TransportSolverTwophaseCompressiblePolymer::SingleCellMethod method;
|
||||
std::string method_string = param.getDefault("single_cell_method", std::string("Bracketing"));
|
||||
if (method_string == "Bracketing") {
|
||||
method = Opm::TransportSolverTwophaseCompressiblePolymer::Bracketing;
|
||||
} else if (method_string == "Newton") {
|
||||
method = Opm::TransportSolverTwophaseCompressiblePolymer::Newton;
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, "Unknown method: " << method_string);
|
||||
}
|
||||
tsolver_.setPreferredMethod(method);
|
||||
num_transport_substeps_ = param.getDefault("num_transport_substeps", 1);
|
||||
use_segregation_split_ = param.getDefault("use_segregation_split", false);
|
||||
if (gravity != 0 && use_segregation_split_) {
|
||||
tsolver_.initGravity(gravity);
|
||||
extractColumn(grid_, columns_);
|
||||
}
|
||||
|
||||
// Misc init.
|
||||
const int num_cells = grid.number_of_cells;
|
||||
allcells_.resize(num_cells);
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
allcells_[cell] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
SimulatorReport SimulatorCompressiblePolymer::Impl::run(SimulatorTimer& timer,
|
||||
PolymerBlackoilState& state,
|
||||
WellState& well_state)
|
||||
{
|
||||
std::vector<double> transport_src(grid_.number_of_cells);
|
||||
std::vector<double> polymer_inflow_c(grid_.number_of_cells);
|
||||
|
||||
// Initialisation.
|
||||
std::vector<double> initial_pressure;
|
||||
std::vector<double> porevol;
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
|
||||
} else {
|
||||
computePorevolume(grid_, props_.porosity(), porevol);
|
||||
}
|
||||
const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0);
|
||||
std::vector<double> initial_porevol = porevol;
|
||||
|
||||
// Main simulation loop.
|
||||
Opm::time::StopWatch pressure_timer;
|
||||
double ptime = 0.0;
|
||||
Opm::time::StopWatch transport_timer;
|
||||
double ttime = 0.0;
|
||||
Opm::time::StopWatch total_timer;
|
||||
total_timer.start();
|
||||
double init_surfvol[2] = { 0.0 };
|
||||
double inplace_surfvol[2] = { 0.0 };
|
||||
double polymass = computePolymerMass(porevol, state.saturation(), state.concentration(), poly_props_.deadPoreVol());
|
||||
double polymass_adsorbed = computePolymerAdsorbed(grid_, props_, poly_props_, state, rock_comp_props_);
|
||||
double init_polymass = polymass + polymass_adsorbed;
|
||||
double tot_injected[2] = { 0.0 };
|
||||
double tot_produced[2] = { 0.0 };
|
||||
double tot_polyinj = 0.0;
|
||||
double tot_polyprod = 0.0;
|
||||
Opm::computeSaturatedVol(porevol, state.surfacevol(), init_surfvol);
|
||||
Opm::Watercut watercut;
|
||||
watercut.push(0.0, 0.0, 0.0);
|
||||
Opm::WellReport wellreport;
|
||||
std::vector<double> fractional_flows;
|
||||
std::vector<double> well_resflows_phase;
|
||||
if (wells_) {
|
||||
well_resflows_phase.resize((wells_->number_of_phases)*(wells_->number_of_wells), 0.0);
|
||||
wellreport.push(props_, *wells_, state.pressure(), state.surfacevol(),
|
||||
state.saturation(), 0.0, well_state.bhp(), well_state.perfRates());
|
||||
}
|
||||
// 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.temperature(), 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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(), state.temperature(), &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_) {
|
||||
outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_);
|
||||
}
|
||||
outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_);
|
||||
outputWaterCut(watercut, output_dir_);
|
||||
if (wells_) {
|
||||
outputWellReport(wellreport, output_dir_);
|
||||
}
|
||||
}
|
||||
|
||||
total_timer.stop();
|
||||
|
||||
SimulatorReport report;
|
||||
report.pressure_time = ptime;
|
||||
report.transport_time = ttime;
|
||||
report.total_time = total_timer.secsSinceStart();
|
||||
return report;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void outputStateVtk(const UnstructuredGrid& grid,
|
||||
const Opm::PolymerBlackoilState& state,
|
||||
const int step,
|
||||
const std::string& output_dir)
|
||||
{
|
||||
// Write data in VTK format.
|
||||
std::ostringstream vtkfilename;
|
||||
vtkfilename << output_dir << "/vtk_files";
|
||||
boost::filesystem::path fpath(vtkfilename.str());
|
||||
try {
|
||||
create_directories(fpath);
|
||||
}
|
||||
catch (...) {
|
||||
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
||||
}
|
||||
vtkfilename << "/output-" << std::setw(5) << std::setfill('0') << step << ".vtu";
|
||||
std::ofstream vtkfile(vtkfilename.str().c_str());
|
||||
if (!vtkfile) {
|
||||
OPM_THROW(std::runtime_error, "Failed to open " << vtkfilename.str());
|
||||
}
|
||||
Opm::DataMap dm;
|
||||
dm["saturation"] = &state.saturation();
|
||||
dm["pressure"] = &state.pressure();
|
||||
dm["concentration"] = &state.concentration();
|
||||
dm["cmax"] = &state.maxconcentration();
|
||||
dm["surfvol"] = &state.surfacevol();
|
||||
std::vector<double> cell_velocity;
|
||||
Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity);
|
||||
dm["velocity"] = &cell_velocity;
|
||||
Opm::writeVtkData(grid, dm, vtkfile);
|
||||
}
|
||||
|
||||
void outputStateMatlab(const UnstructuredGrid& grid,
|
||||
const Opm::PolymerBlackoilState& state,
|
||||
const int step,
|
||||
const std::string& output_dir)
|
||||
{
|
||||
Opm::DataMap dm;
|
||||
dm["saturation"] = &state.saturation();
|
||||
dm["pressure"] = &state.pressure();
|
||||
dm["concentration"] = &state.concentration();
|
||||
dm["cmax"] = &state.maxconcentration();
|
||||
dm["surfvol"] = &state.surfacevol();
|
||||
std::vector<double> cell_velocity;
|
||||
Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity);
|
||||
dm["velocity"] = &cell_velocity;
|
||||
|
||||
// Write data (not grid) in Matlab format
|
||||
for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) {
|
||||
std::ostringstream fname;
|
||||
fname << output_dir << "/" << it->first;
|
||||
boost::filesystem::path fpath = fname.str();
|
||||
try {
|
||||
create_directories(fpath);
|
||||
}
|
||||
catch (...) {
|
||||
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
||||
}
|
||||
fname << "/" << std::setw(5) << std::setfill('0') << step << ".txt";
|
||||
std::ofstream file(fname.str().c_str());
|
||||
if (!file) {
|
||||
OPM_THROW(std::runtime_error, "Failed to open " << fname.str());
|
||||
}
|
||||
const std::vector<double>& d = *(it->second);
|
||||
std::copy(d.begin(), d.end(), std::ostream_iterator<double>(file, "\n"));
|
||||
}
|
||||
}
|
||||
|
||||
void outputWaterCut(const Opm::Watercut& watercut,
|
||||
const std::string& output_dir)
|
||||
{
|
||||
// Write water cut curve.
|
||||
std::string fname = output_dir + "/watercut.txt";
|
||||
std::ofstream os(fname.c_str());
|
||||
if (!os) {
|
||||
OPM_THROW(std::runtime_error, "Failed to open " << fname);
|
||||
}
|
||||
watercut.write(os);
|
||||
}
|
||||
|
||||
|
||||
void outputWellReport(const Opm::WellReport& wellreport,
|
||||
const std::string& output_dir)
|
||||
{
|
||||
// Write well report.
|
||||
std::string fname = output_dir + "/wellreport.txt";
|
||||
std::ofstream os(fname.c_str());
|
||||
if (!os) {
|
||||
OPM_THROW(std::runtime_error, "Failed to open " << fname);
|
||||
}
|
||||
wellreport.write(os);
|
||||
}
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
102
opm/polymer/SimulatorCompressiblePolymer.hpp
Normal file
102
opm/polymer/SimulatorCompressiblePolymer.hpp
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_SIMULATORCOMPRESSIBLEPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_SIMULATORCOMPRESSIBLEPOLYMER_HEADER_INCLUDED
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <vector>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
struct Wells;
|
||||
struct FlowBoundaryConditions;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
namespace parameter { class ParameterGroup; }
|
||||
class BlackoilPropertiesInterface;
|
||||
class PolymerProperties;
|
||||
class RockCompressibility;
|
||||
class WellsManager;
|
||||
class PolymerInflowInterface;
|
||||
class LinearSolverInterface;
|
||||
class SimulatorTimer;
|
||||
class PolymerBlackoilState;
|
||||
class WellState;
|
||||
struct SimulatorReport;
|
||||
|
||||
/// Class collecting all necessary components for a two-phase simulation.
|
||||
class SimulatorCompressiblePolymer
|
||||
{
|
||||
public:
|
||||
/// Initialise from parameters and objects to observe.
|
||||
/// \param[in] param parameters, this class accepts the following:
|
||||
/// parameter (default) effect
|
||||
/// -----------------------------------------------------------
|
||||
/// output (true) write output to files?
|
||||
/// output_dir ("output") output directoty
|
||||
/// output_interval (1) output every nth step
|
||||
/// nl_pressure_residual_tolerance (0.0) pressure solver residual tolerance (in Pascal)
|
||||
/// nl_pressure_change_tolerance (1.0) pressure solver change tolerance (in Pascal)
|
||||
/// nl_pressure_maxiter (10) max nonlinear iterations in pressure
|
||||
/// nl_maxiter (30) max nonlinear iterations in transport
|
||||
/// nl_tolerance (1e-9) transport solver absolute residual tolerance
|
||||
/// num_transport_substeps (1) number of transport steps per pressure step
|
||||
/// use_segregation_split (false) solve for gravity segregation (if false,
|
||||
/// segregation is ignored).
|
||||
///
|
||||
/// \param[in] grid grid data structure
|
||||
/// \param[in] props fluid and rock properties
|
||||
/// \param[in] poly_props polymer properties
|
||||
/// \param[in] rock_comp_props if non-null, rock compressibility properties
|
||||
/// \param[in] wells_manager well manager, may manage no (null) wells
|
||||
/// \param[in] polymer_inflow polymer inflow controls
|
||||
/// \param[in] linsolver linear solver
|
||||
/// \param[in] gravity if non-null, gravity vector
|
||||
SimulatorCompressiblePolymer(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const PolymerProperties& poly_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const PolymerInflowInterface& polymer_inflow,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double* gravity);
|
||||
|
||||
/// Run the simulation.
|
||||
/// This will run succesive timesteps until timer.done() is true. It will
|
||||
/// modify the reservoir and well states.
|
||||
/// \param[in,out] timer governs the requested reporting timesteps
|
||||
/// \param[in,out] state state of reservoir: pressure, fluxes, polymer concentration,
|
||||
/// saturations, surface volumes.
|
||||
/// \param[in,out] well_state state of wells: bhp, perforation rates
|
||||
/// \return simulation report, with timing data
|
||||
SimulatorReport run(SimulatorTimer& timer,
|
||||
PolymerBlackoilState& state,
|
||||
WellState& well_state);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
// Using shared_ptr instead of scoped_ptr since scoped_ptr requires complete type for Impl.
|
||||
boost::shared_ptr<Impl> pimpl_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_SIMULATORCOMPRESSIBLEPOLYMER_HEADER_INCLUDED
|
690
opm/polymer/SimulatorPolymer.cpp
Normal file
690
opm/polymer/SimulatorPolymer.cpp
Normal file
@ -0,0 +1,690 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/polymer/SimulatorPolymer.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
|
||||
#include <opm/polymer/IncompTpfaPolymer.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
#include <opm/core/pressure/flow_bc.h>
|
||||
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/StopWatch.hpp>
|
||||
#include <opm/core/io/vtk/writeVtkData.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
|
||||
#include <opm/core/props/IncompPropertiesInterface.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/core/grid/ColumnExtract.hpp>
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <opm/polymer/PolymerState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/polymer/TransportSolverTwophasePolymer.hpp>
|
||||
#include <opm/polymer/PolymerInflow.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
#include <opm/polymer/polymerUtilities.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef HAVE_ERT
|
||||
#include <opm/core/io/eclipse/writeECLData.hpp>
|
||||
#endif
|
||||
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
void outputStateVtk(const UnstructuredGrid& grid,
|
||||
const Opm::PolymerState& state,
|
||||
const int step,
|
||||
const std::string& output_dir);
|
||||
void outputStateBinary(const UnstructuredGrid& grid,
|
||||
const Opm::PolymerState& state,
|
||||
const SimulatorTimer& simtimer,
|
||||
const std::string& output_dir);
|
||||
void outputStateMatlab(const UnstructuredGrid& grid,
|
||||
const Opm::PolymerState& state,
|
||||
const int step,
|
||||
const std::string& output_dir);
|
||||
void outputWaterCut(const Opm::Watercut& watercut,
|
||||
const std::string& output_dir);
|
||||
void outputWellReport(const Opm::WellReport& wellreport,
|
||||
const std::string& output_dir);
|
||||
bool allNeumannBCs(const FlowBoundaryConditions* bcs);
|
||||
bool allRateWells(const Wells* wells);
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
||||
class SimulatorPolymer::Impl
|
||||
{
|
||||
public:
|
||||
Impl(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const PolymerProperties& poly_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const PolymerInflowInterface& polymer_inflow,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double* gravity);
|
||||
|
||||
SimulatorReport run(SimulatorTimer& timer,
|
||||
PolymerState& state,
|
||||
WellState& well_state);
|
||||
|
||||
private:
|
||||
// Data.
|
||||
|
||||
// Parameters for output.
|
||||
bool output_;
|
||||
bool output_vtk_;
|
||||
bool output_binary_;
|
||||
std::string output_dir_;
|
||||
int output_interval_;
|
||||
// Parameters for well control
|
||||
bool check_well_controls_;
|
||||
int max_well_control_iterations_;
|
||||
// Parameters for transport solver.
|
||||
int num_transport_substeps_;
|
||||
bool use_segregation_split_;
|
||||
// Observed objects.
|
||||
const UnstructuredGrid& grid_;
|
||||
const IncompPropertiesInterface& props_;
|
||||
const PolymerProperties& poly_props_;
|
||||
const RockCompressibility* rock_comp_props_;
|
||||
WellsManager& wells_manager_;
|
||||
const Wells* wells_;
|
||||
const PolymerInflowInterface& polymer_inflow_;
|
||||
const std::vector<double>& src_;
|
||||
const FlowBoundaryConditions* bcs_;
|
||||
// Solvers
|
||||
IncompTpfaPolymer psolver_;
|
||||
TransportSolverTwophasePolymer tsolver_;
|
||||
// Needed by column-based gravity segregation solver.
|
||||
std::vector< std::vector<int> > columns_;
|
||||
// Misc. data
|
||||
std::vector<int> allcells_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
SimulatorPolymer::SimulatorPolymer(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const PolymerProperties& poly_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const PolymerInflowInterface& polymer_inflow,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double* gravity)
|
||||
{
|
||||
pimpl_.reset(new Impl(param, grid, props, poly_props, rock_comp_props,
|
||||
wells_manager, polymer_inflow, src, bcs, linsolver, gravity));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
SimulatorReport SimulatorPolymer::run(SimulatorTimer& timer,
|
||||
PolymerState& state,
|
||||
WellState& well_state)
|
||||
{
|
||||
return pimpl_->run(timer, state, well_state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SimulatorPolymer::Impl::Impl(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const PolymerProperties& poly_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const PolymerInflowInterface& polymer_inflow,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double* gravity)
|
||||
: grid_(grid),
|
||||
props_(props),
|
||||
poly_props_(poly_props),
|
||||
rock_comp_props_(rock_comp_props),
|
||||
wells_manager_(wells_manager),
|
||||
wells_(wells_manager.c_wells()),
|
||||
polymer_inflow_(polymer_inflow),
|
||||
src_(src),
|
||||
bcs_(bcs),
|
||||
psolver_(grid, props, rock_comp_props, poly_props, linsolver,
|
||||
param.getDefault("nl_pressure_residual_tolerance", 0.0),
|
||||
param.getDefault("nl_pressure_change_tolerance", 1.0),
|
||||
param.getDefault("nl_pressure_maxiter", 10),
|
||||
gravity, wells_manager.c_wells(), src, bcs),
|
||||
tsolver_(grid, props, poly_props, TransportSolverTwophasePolymer::Bracketing,
|
||||
param.getDefault("nl_tolerance", 1e-9),
|
||||
param.getDefault("nl_maxiter", 30))
|
||||
{
|
||||
// For output.
|
||||
output_ = param.getDefault("output", true);
|
||||
if (output_) {
|
||||
output_vtk_ = param.getDefault("output_vtk", true);
|
||||
output_binary_ = param.getDefault("output_binary", false);
|
||||
#ifndef HAVE_ERT
|
||||
if (output_binary_) {
|
||||
OPM_THROW(std::runtime_error, "Cannot make binary output without ert library support. Reconfigure opm-core and opm-polymer with --with-ert and recompile.");
|
||||
}
|
||||
#endif
|
||||
output_dir_ = param.getDefault("output_dir", std::string("output"));
|
||||
// Ensure that output dir exists
|
||||
boost::filesystem::path fpath(output_dir_);
|
||||
try {
|
||||
create_directories(fpath);
|
||||
}
|
||||
catch (...) {
|
||||
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
||||
}
|
||||
output_interval_ = param.getDefault("output_interval", 1);
|
||||
}
|
||||
|
||||
// Well control related init.
|
||||
check_well_controls_ = param.getDefault("check_well_controls", false);
|
||||
max_well_control_iterations_ = param.getDefault("max_well_control_iterations", 10);
|
||||
|
||||
// Transport related init.
|
||||
TransportSolverTwophasePolymer::SingleCellMethod method;
|
||||
std::string method_string = param.getDefault("single_cell_method", std::string("Bracketing"));
|
||||
if (method_string == "Bracketing") {
|
||||
method = Opm::TransportSolverTwophasePolymer::Bracketing;
|
||||
} else if (method_string == "Newton") {
|
||||
method = Opm::TransportSolverTwophasePolymer::Newton;
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, "Unknown method: " << method_string);
|
||||
}
|
||||
tsolver_.setPreferredMethod(method);
|
||||
num_transport_substeps_ = param.getDefault("num_transport_substeps", 1);
|
||||
use_segregation_split_ = param.getDefault("use_segregation_split", false);
|
||||
if (gravity != 0 && use_segregation_split_) {
|
||||
tsolver_.initGravity(gravity);
|
||||
extractColumn(grid_, columns_);
|
||||
}
|
||||
|
||||
// Misc init.
|
||||
const int num_cells = grid.number_of_cells;
|
||||
allcells_.resize(num_cells);
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
allcells_[cell] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
SimulatorReport SimulatorPolymer::Impl::run(SimulatorTimer& timer,
|
||||
PolymerState& state,
|
||||
WellState& well_state)
|
||||
{
|
||||
std::vector<double> transport_src(grid_.number_of_cells);
|
||||
std::vector<double> polymer_inflow_c(grid_.number_of_cells);
|
||||
|
||||
// Initialisation.
|
||||
std::vector<double> porevol;
|
||||
if (rock_comp_props_ && rock_comp_props_->isActive()) {
|
||||
computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol);
|
||||
} else {
|
||||
computePorevolume(grid_, props_.porosity(), porevol);
|
||||
}
|
||||
const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0);
|
||||
std::vector<double> initial_porevol = porevol;
|
||||
|
||||
// Main simulation loop.
|
||||
Opm::time::StopWatch pressure_timer;
|
||||
double ptime = 0.0;
|
||||
Opm::time::StopWatch transport_timer;
|
||||
double ttime = 0.0;
|
||||
Opm::time::StopWatch total_timer;
|
||||
total_timer.start();
|
||||
double init_satvol[2] = { 0.0 };
|
||||
double satvol[2] = { 0.0 };
|
||||
double polymass = computePolymerMass(porevol, state.saturation(), state.concentration(), poly_props_.deadPoreVol());
|
||||
double polymass_adsorbed = computePolymerAdsorbed(props_, poly_props_, porevol, state.maxconcentration());
|
||||
double init_polymass = polymass + polymass_adsorbed;
|
||||
double injected[2] = { 0.0 };
|
||||
double produced[2] = { 0.0 };
|
||||
double polyinj = 0.0;
|
||||
double polyprod = 0.0;
|
||||
double tot_injected[2] = { 0.0 };
|
||||
double tot_produced[2] = { 0.0 };
|
||||
double tot_polyinj = 0.0;
|
||||
double tot_polyprod = 0.0;
|
||||
Opm::computeSaturatedVol(porevol, state.saturation(), init_satvol);
|
||||
std::cout << "\nInitial saturations are " << init_satvol[0]/tot_porevol_init
|
||||
<< " " << init_satvol[1]/tot_porevol_init << std::endl;
|
||||
Opm::Watercut watercut;
|
||||
watercut.push(0.0, 0.0, 0.0);
|
||||
Opm::WellReport wellreport;
|
||||
std::vector<double> fractional_flows;
|
||||
std::vector<double> well_resflows_phase;
|
||||
if (wells_) {
|
||||
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());
|
||||
}
|
||||
// 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<double> 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
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_) {
|
||||
outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_);
|
||||
}
|
||||
if (output_binary_) {
|
||||
outputStateBinary(grid_, state, timer, output_dir_);
|
||||
}
|
||||
outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_);
|
||||
outputWaterCut(watercut, output_dir_);
|
||||
if (wells_) {
|
||||
outputWellReport(wellreport, output_dir_);
|
||||
}
|
||||
}
|
||||
|
||||
total_timer.stop();
|
||||
|
||||
SimulatorReport report;
|
||||
report.pressure_time = ptime;
|
||||
report.transport_time = ttime;
|
||||
report.total_time = total_timer.secsSinceStart();
|
||||
return report;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void outputStateVtk(const UnstructuredGrid& grid,
|
||||
const Opm::PolymerState& state,
|
||||
const int step,
|
||||
const std::string& output_dir)
|
||||
{
|
||||
// Write data in VTK format.
|
||||
std::ostringstream vtkfilename;
|
||||
vtkfilename << output_dir << "/vtk_files";
|
||||
boost::filesystem::path fpath(vtkfilename.str());
|
||||
try {
|
||||
create_directories(fpath);
|
||||
}
|
||||
catch (...) {
|
||||
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
||||
}
|
||||
vtkfilename << "/output-" << std::setw(5) << std::setfill('0') << step << ".vtu";
|
||||
std::ofstream vtkfile(vtkfilename.str().c_str());
|
||||
if (!vtkfile) {
|
||||
OPM_THROW(std::runtime_error, "Failed to open " << vtkfilename.str());
|
||||
}
|
||||
Opm::DataMap dm;
|
||||
dm["saturation"] = &state.saturation();
|
||||
dm["pressure"] = &state.pressure();
|
||||
dm["concentration"] = &state.concentration();
|
||||
dm["cmax"] = &state.maxconcentration();
|
||||
std::vector<double> cell_velocity;
|
||||
Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity);
|
||||
dm["velocity"] = &cell_velocity;
|
||||
Opm::writeVtkData(grid, dm, vtkfile);
|
||||
}
|
||||
|
||||
void outputStateMatlab(const UnstructuredGrid& grid,
|
||||
const Opm::PolymerState& state,
|
||||
const int step,
|
||||
const std::string& output_dir)
|
||||
{
|
||||
Opm::DataMap dm;
|
||||
dm["saturation"] = &state.saturation();
|
||||
dm["pressure"] = &state.pressure();
|
||||
dm["concentration"] = &state.concentration();
|
||||
dm["cmax"] = &state.maxconcentration();
|
||||
std::vector<double> cell_velocity;
|
||||
Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity);
|
||||
dm["velocity"] = &cell_velocity;
|
||||
|
||||
// Write data (not grid) in Matlab format
|
||||
for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) {
|
||||
std::ostringstream fname;
|
||||
fname << output_dir << "/" << it->first;
|
||||
boost::filesystem::path fpath = fname.str();
|
||||
try {
|
||||
create_directories(fpath);
|
||||
}
|
||||
catch (...) {
|
||||
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
||||
}
|
||||
fname << "/" << std::setw(5) << std::setfill('0') << step << ".txt";
|
||||
std::ofstream file(fname.str().c_str());
|
||||
if (!file) {
|
||||
OPM_THROW(std::runtime_error, "Failed to open " << fname.str());
|
||||
}
|
||||
const std::vector<double>& d = *(it->second);
|
||||
std::copy(d.begin(), d.end(), std::ostream_iterator<double>(file, "\n"));
|
||||
}
|
||||
}
|
||||
|
||||
void outputStateBinary(const UnstructuredGrid& grid,
|
||||
const Opm::PolymerState& state,
|
||||
const SimulatorTimer& simtimer,
|
||||
const std::string& output_dir)
|
||||
{
|
||||
#ifdef HAVE_ERT
|
||||
Opm::DataMap dm;
|
||||
dm["saturation"] = &state.saturation();
|
||||
dm["pressure"] = &state.pressure();
|
||||
dm["concentration"] = &state.concentration();
|
||||
dm["cmax"] = &state.maxconcentration();
|
||||
std::vector<double> cell_velocity;
|
||||
Opm::estimateCellVelocity(grid, state.faceflux(), cell_velocity);
|
||||
dm["velocity"] = &cell_velocity;
|
||||
|
||||
writeECLData(grid, dm, simtimer.currentStepNum(), simtimer.simulationTimeElapsed(), simtimer.currentDateTime(),
|
||||
output_dir, "polymer_ecl");
|
||||
#else
|
||||
OPM_THROW(std::runtime_error, "Cannot call outputStateBinary() without ert library support. Reconfigure with --with-ert and recompile.");
|
||||
#endif
|
||||
}
|
||||
|
||||
void outputWaterCut(const Opm::Watercut& watercut,
|
||||
const std::string& output_dir)
|
||||
{
|
||||
// Write water cut curve.
|
||||
std::string fname = output_dir + "/watercut.txt";
|
||||
std::ofstream os(fname.c_str());
|
||||
if (!os) {
|
||||
OPM_THROW(std::runtime_error, "Failed to open " << fname);
|
||||
}
|
||||
watercut.write(os);
|
||||
}
|
||||
|
||||
|
||||
void outputWellReport(const Opm::WellReport& wellreport,
|
||||
const std::string& output_dir)
|
||||
{
|
||||
// Write well report.
|
||||
std::string fname = output_dir + "/wellreport.txt";
|
||||
std::ofstream os(fname.c_str());
|
||||
if (!os) {
|
||||
OPM_THROW(std::runtime_error, "Failed to open " << fname);
|
||||
}
|
||||
wellreport.write(os);
|
||||
}
|
||||
|
||||
|
||||
bool allNeumannBCs(const FlowBoundaryConditions* bcs)
|
||||
{
|
||||
if (bcs == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
return std::find(bcs->type, bcs->type + bcs->nbc, BC_PRESSURE)
|
||||
== bcs->type + bcs->nbc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool allRateWells(const Wells* wells)
|
||||
{
|
||||
if (wells == NULL) {
|
||||
return true;
|
||||
}
|
||||
const int nw = wells->number_of_wells;
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
const WellControls* wc = wells->ctrls[w];
|
||||
if (well_controls_get_current( wc ) >= 0) {
|
||||
if (well_controls_get_current_type(wc) == BHP) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
106
opm/polymer/SimulatorPolymer.hpp
Normal file
106
opm/polymer/SimulatorPolymer.hpp
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_SIMULATORPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_SIMULATORPOLYMER_HEADER_INCLUDED
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <vector>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
struct Wells;
|
||||
struct FlowBoundaryConditions;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
namespace parameter { class ParameterGroup; }
|
||||
class IncompPropertiesInterface;
|
||||
class PolymerProperties;
|
||||
class RockCompressibility;
|
||||
class WellsManager;
|
||||
class PolymerInflowInterface;
|
||||
class LinearSolverInterface;
|
||||
class SimulatorTimer;
|
||||
class PolymerState;
|
||||
class WellState;
|
||||
struct SimulatorReport;
|
||||
|
||||
/// Class collecting all necessary components for a two-phase simulation.
|
||||
class SimulatorPolymer
|
||||
{
|
||||
public:
|
||||
/// Initialise from parameters and objects to observe.
|
||||
/// \param[in] param parameters, this class accepts the following:
|
||||
/// parameter (default) effect
|
||||
/// -----------------------------------------------------------
|
||||
/// output (true) write output to files?
|
||||
/// output_dir ("output") output directoty
|
||||
/// output_interval (1) output every nth step
|
||||
/// nl_pressure_residual_tolerance (0.0) pressure solver residual tolerance (in Pascal)
|
||||
/// nl_pressure_change_tolerance (1.0) pressure solver change tolerance (in Pascal)
|
||||
/// nl_pressure_maxiter (10) max nonlinear iterations in pressure
|
||||
/// nl_maxiter (30) max nonlinear iterations in transport
|
||||
/// nl_tolerance (1e-9) transport solver absolute residual tolerance
|
||||
/// num_transport_substeps (1) number of transport steps per pressure step
|
||||
/// use_segregation_split (false) solve for gravity segregation (if false,
|
||||
/// segregation is ignored).
|
||||
///
|
||||
/// \param[in] grid grid data structure
|
||||
/// \param[in] props fluid and rock properties
|
||||
/// \param[in] poly_props polymer properties
|
||||
/// \param[in] rock_comp_props if non-null, rock compressibility properties
|
||||
/// \param[in] wells_manager well manager, may manage no (null) wells
|
||||
/// \param[in] polymer_inflow polymer inflow controls
|
||||
/// \param[in] src source terms
|
||||
/// \param[in] bcs boundary conditions, treat as all noflow if null
|
||||
/// \param[in] linsolver linear solver
|
||||
/// \param[in] gravity if non-null, gravity vector
|
||||
SimulatorPolymer(const parameter::ParameterGroup& param,
|
||||
const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const PolymerProperties& poly_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
WellsManager& wells_manager,
|
||||
const PolymerInflowInterface& polymer_inflow,
|
||||
const std::vector<double>& src,
|
||||
const FlowBoundaryConditions* bcs,
|
||||
LinearSolverInterface& linsolver,
|
||||
const double* gravity);
|
||||
|
||||
/// Run the simulation.
|
||||
/// This will run succesive timesteps until timer.done() is true. It will
|
||||
/// modify the reservoir and well states.
|
||||
/// \param[in,out] timer governs the requested reporting timesteps
|
||||
/// \param[in,out] state state of reservoir: pressure, fluxes, polymer concentration,
|
||||
/// saturations.
|
||||
/// \param[in,out] well_state state of wells: bhp, perforation rates
|
||||
/// \return simulation report, with timing data
|
||||
SimulatorReport run(SimulatorTimer& timer,
|
||||
PolymerState& state,
|
||||
WellState& well_state);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
// Using shared_ptr instead of scoped_ptr since scoped_ptr requires complete type for Impl.
|
||||
boost::shared_ptr<Impl> pimpl_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_SIMULATORPOLYMER_HEADER_INCLUDED
|
713
opm/polymer/SinglePointUpwindTwoPhasePolymer.hpp
Normal file
713
opm/polymer/SinglePointUpwindTwoPhasePolymer.hpp
Normal file
@ -0,0 +1,713 @@
|
||||
/*===========================================================================
|
||||
//
|
||||
// File: SinglePointUpwindTwoPhase.hpp
|
||||
//
|
||||
// Created: 2011-09-28 14:21:34+0200
|
||||
//
|
||||
// Authors: Ingeborg S. Ligaarden <Ingeborg.Ligaarden@sintef.no>
|
||||
// Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
// Halvor M. Nilsen <HalvorMoll.Nilsen@sintef.no>
|
||||
// Atgeirr F. Rasmussen <atgeirr@sintef.no>
|
||||
// Bård Skaflestad <Bard.Skaflestad@sintef.no>
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
|
||||
/*
|
||||
Copyright 2011 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2011 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_SINGLEPOINTUPWINDTWOPHASEPOLYMER_HPP_HEADER
|
||||
#define OPM_SINGLEPOINTUPWINDTWOPHASEPOLYMER_HPP_HEADER
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
namespace Opm {
|
||||
namespace polymer_reorder {
|
||||
class ModelParameterStorage {
|
||||
public:
|
||||
ModelParameterStorage(int nc, int totconn)
|
||||
: drho_(0.0), rockdensity_(0.0), mob_(0),
|
||||
dmobds_(0), dmobwatdc_(0), mc_(0),
|
||||
dmcdc_(0), porevol_(0), porosity_(0), dg_(0), sw_(0), c_(0), cmax_(0),
|
||||
ds_(0), dsc_(0), dcads_(0), dcadsdc_(0), pc_(0), dpc_(0),
|
||||
trans_(0), data_()
|
||||
{
|
||||
size_t alloc_sz;
|
||||
|
||||
alloc_sz = 2 * nc; // mob_
|
||||
alloc_sz += 2 * nc; // dmobds_
|
||||
alloc_sz += nc; // dmobwatdc_
|
||||
alloc_sz += nc; // mc_
|
||||
alloc_sz += nc; // dmcdc_
|
||||
alloc_sz += 1 * nc; // porevol_
|
||||
alloc_sz += 1 * nc; // porosity_
|
||||
alloc_sz += 1 * totconn; // dg_
|
||||
alloc_sz += 1 * nc; // sw_
|
||||
alloc_sz += 1 * nc; // c_
|
||||
alloc_sz += 1 * nc; // cmax_
|
||||
alloc_sz += 1 * nc; // ds_
|
||||
alloc_sz += 1 * nc; // dsc_
|
||||
alloc_sz += 1 * nc; // dcads_
|
||||
alloc_sz += 1 * nc; // dcadsdc_
|
||||
alloc_sz += 1 * nc; // pc_
|
||||
alloc_sz += 1 * nc; // dpc_
|
||||
alloc_sz += 1 * totconn; // trans_
|
||||
data_.resize(alloc_sz);
|
||||
|
||||
mob_ = &data_[0];
|
||||
dmobds_ = mob_ + (2 * nc );
|
||||
dmobwatdc_ = dmobds_ + (2 * nc );
|
||||
mc_ = dmobwatdc_ + (1 * nc );
|
||||
dmcdc_ = mc_ + (1 * nc );
|
||||
porevol_ = dmcdc_ + (1 * nc );
|
||||
porosity_ = porevol_ + (1 * nc );
|
||||
dg_ = porosity_ + (1 * nc );
|
||||
sw_ = dg_ + (1 * totconn);
|
||||
c_ = sw_ + (1 * nc );
|
||||
cmax_ = c_ + (1 * nc );
|
||||
ds_ = cmax_ + (1 * nc );
|
||||
dsc_ = ds_ + (1 * nc );
|
||||
dcads_ = dsc_ + (1 * nc );
|
||||
dcadsdc_ = dcads_ + (1 * nc );
|
||||
pc_ = dcadsdc_ + (1 * nc );
|
||||
dpc_ = pc_ + (1 * nc );
|
||||
trans_ = dpc_ + (1 * nc );
|
||||
}
|
||||
|
||||
double& drho () { return drho_ ; }
|
||||
double drho () const { return drho_ ; }
|
||||
|
||||
double& rockdensity() { return rockdensity_ ; }
|
||||
double rockdensity() const { return rockdensity_ ; }
|
||||
|
||||
double* mob (int cell) { return mob_ + (2*cell + 0); }
|
||||
const double* mob (int cell) const { return mob_ + (2*cell + 0); }
|
||||
|
||||
double* dmobds (int cell) { return dmobds_ + (2*cell + 0); }
|
||||
const double* dmobds (int cell) const { return dmobds_ + (2*cell + 0); }
|
||||
|
||||
double& dmobwatdc (int cell) { return dmobwatdc_[cell]; }
|
||||
double dmobwatdc (int cell) const { return dmobwatdc_[cell]; }
|
||||
|
||||
double& mc (int cell) { return mc_[cell]; }
|
||||
double mc (int cell) const { return mc_[cell]; }
|
||||
|
||||
double& dmcdc (int cell) { return dmcdc_[cell]; }
|
||||
double dmcdc (int cell) const { return dmcdc_[cell]; }
|
||||
|
||||
double* porevol() { return porevol_ ; }
|
||||
double porevol(int cell) const { return porevol_[cell] ; }
|
||||
|
||||
double* porosity() { return porosity_ ; }
|
||||
double porosity(int cell) const { return porosity_[cell] ; }
|
||||
|
||||
|
||||
double& dg(int i) { return dg_[i] ; }
|
||||
double dg(int i) const { return dg_[i] ; }
|
||||
|
||||
double& sw(int cell) { return sw_[cell] ; }
|
||||
double sw(int cell) const { return sw_[cell] ; }
|
||||
|
||||
double& c(int cell) { return c_[cell] ; }
|
||||
double c(int cell) const { return c_[cell] ; }
|
||||
|
||||
double& cmax(int cell) { return cmax_[cell] ; }
|
||||
double cmax(int cell) const { return cmax_[cell] ; }
|
||||
|
||||
double& ds(int cell) { return ds_[cell] ; }
|
||||
double ds(int cell) const { return ds_[cell] ; }
|
||||
|
||||
double& dsc(int cell) { return dsc_[cell] ; }
|
||||
double dsc(int cell) const { return dsc_[cell] ; }
|
||||
|
||||
double& dcads(int cell) { return dcads_[cell] ; }
|
||||
double dcads(int cell) const { return dcads_[cell] ; }
|
||||
|
||||
double& dcadsdc(int cell) { return dcadsdc_[cell] ; }
|
||||
double dcadsdc(int cell) const { return dcadsdc_[cell] ; }
|
||||
|
||||
double& pc(int cell) { return pc_[cell] ; }
|
||||
double pc(int cell) const { return pc_[cell] ; }
|
||||
|
||||
double& dpc(int cell) { return dpc_[cell] ; }
|
||||
double dpc(int cell) const { return dpc_[cell] ; }
|
||||
|
||||
double& trans(int f) { return trans_[f] ; }
|
||||
double trans(int f) const { return trans_[f] ; }
|
||||
|
||||
private:
|
||||
double drho_ ;
|
||||
double rockdensity_ ;
|
||||
double *mob_ ;
|
||||
double *dmobds_ ;
|
||||
double *dmobwatdc_ ;
|
||||
double *mc_ ;
|
||||
double *dmcdc_ ;
|
||||
double *porevol_ ;
|
||||
double *porosity_ ;
|
||||
double *dg_ ;
|
||||
double *sw_ ;
|
||||
double *c_ ;
|
||||
double *cmax_ ;
|
||||
double *ds_ ;
|
||||
double *dsc_ ;
|
||||
double *dcads_ ; // difference of cads to compute residual
|
||||
double *dcadsdc_ ; // derivative of cads
|
||||
double *pc_ ;
|
||||
double *dpc_ ;
|
||||
double *trans_ ;
|
||||
|
||||
std::vector<double> data_;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
template <class TwophaseFluidPolymer>
|
||||
class SinglePointUpwindTwoPhasePolymer {
|
||||
public:
|
||||
template <class Grid>
|
||||
SinglePointUpwindTwoPhasePolymer(const TwophaseFluidPolymer& fluid ,
|
||||
const Grid& g ,
|
||||
const std::vector<double>& porevol ,
|
||||
const double* grav = 0,
|
||||
const bool guess_previous = true)
|
||||
: fluid_ (fluid) ,
|
||||
gravity_ (grav) ,
|
||||
f2hf_ (2 * g.number_of_faces, -1) ,
|
||||
store_ (g.number_of_cells,
|
||||
g.cell_facepos[ g.number_of_cells ]),
|
||||
init_step_use_previous_sol_(guess_previous) ,
|
||||
sat_tol_ (1e-5)
|
||||
{
|
||||
|
||||
if (gravity_) {
|
||||
store_.drho() = fluid_.density(0) - fluid_.density(1);
|
||||
}
|
||||
|
||||
for (int c = 0, i = 0; c < g.number_of_cells; ++c) {
|
||||
for (; i < g.cell_facepos[c + 1]; ++i) {
|
||||
const int f = g.cell_faces[i];
|
||||
const int p = 1 - (g.face_cells[2*f + 0] == c);
|
||||
f2hf_[2*f + p] = i;
|
||||
}
|
||||
}
|
||||
|
||||
std::copy(porevol.begin(), porevol.end(), store_.porevol());
|
||||
const double* poro = fluid.porosity();
|
||||
std::copy(poro, poro + g.number_of_cells, store_.porosity());
|
||||
store_.rockdensity() = fluid.rockdensity();
|
||||
}
|
||||
|
||||
void makefhfQPeriodic( const std::vector<int>& p_faces,const std::vector<int>& hf_faces,
|
||||
const std::vector<int>& nb_faces)
|
||||
{
|
||||
std::vector<int> nbhf(hf_faces.size());
|
||||
for(unsigned int i=0; i<p_faces.size(); ++i){
|
||||
int nbf = nb_faces[i];
|
||||
if(f2hf_[2*nbf] == -1){
|
||||
nbhf[i] = f2hf_[2*nbf+1];
|
||||
}else{
|
||||
assert(f2hf_[2*nbf+1]==-1);
|
||||
nbhf[i] = f2hf_[2*nbf];
|
||||
}
|
||||
}
|
||||
for(unsigned int i=0; i<p_faces.size(); ++i){
|
||||
|
||||
int f = p_faces[i];
|
||||
int hf = hf_faces[i];
|
||||
bool changed=false;
|
||||
|
||||
if(f2hf_[2*f] == hf){
|
||||
assert(f2hf_[2*f+1]==-1);
|
||||
}else{
|
||||
assert(f2hf_[2*f]==-1);
|
||||
f2hf_[2*f]=nbhf[i];
|
||||
changed=true;
|
||||
}
|
||||
if(!changed){
|
||||
if(f2hf_[2*f+1]== hf){
|
||||
assert(f2hf_[2*f]==-1);
|
||||
}else{
|
||||
assert(f2hf_[2*f+1]==-1);
|
||||
f2hf_[2*f+1]=nbhf[i];
|
||||
changed=true;
|
||||
}
|
||||
}
|
||||
assert(changed);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// System assembly innards
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
enum { DofPerCell = 1 };
|
||||
|
||||
void
|
||||
initResidual(const int c, double* Fs, double* Fc) const {
|
||||
(void) c; // Suppress 'unused' warning
|
||||
*Fs = 0.0;
|
||||
*Fc = 0.0;
|
||||
}
|
||||
|
||||
template <class ReservoirState,
|
||||
class Grid >
|
||||
void
|
||||
fluxConnection(const ReservoirState& state ,
|
||||
const Grid& g ,
|
||||
const double dt ,
|
||||
const int cell ,
|
||||
const int f ,
|
||||
double* F , // F[0] = s-residual, F[1] = c-residual
|
||||
double* dFd1 , //Jacobi matrix for residual with respect to variables in cell
|
||||
double* dFd2 //Jacobi matrix for residual with respect to variables in OTHER cell
|
||||
//dFd1[0]= d(F[0])/d(s1), dFd1[1]= d(F[0])/d(c1), dFd1[2]= d(F[1])/d(s1), dFd1[3]= d(F[1])/d(c1),
|
||||
//dFd2[0]= d(F[0])/d(s2), dFd2[1]= d(F[0])/d(c2), dFd2[2]= d(F[1])/d(s2), dFd2[3]= d(F[1])/d(c2).
|
||||
|
||||
) const {
|
||||
|
||||
const int *n = g.face_cells + (2 * f);
|
||||
double dflux = state.faceflux()[f];
|
||||
double gflux = gravityFlux(f);
|
||||
double pcflux, dpcflux[2];
|
||||
capFlux(f, n, pcflux, dpcflux);
|
||||
gflux += pcflux;
|
||||
|
||||
int pix[2];
|
||||
double m[2], dmds[2], dmobwatdc;
|
||||
double mc, dmcdc;
|
||||
upwindMobility(dflux, gflux, n, pix, m, dmds, dmobwatdc, mc, dmcdc);
|
||||
|
||||
assert ((m[0] >= 0.0) && (m[1] >= 0.0));
|
||||
|
||||
double mt = m[0] + m[1];
|
||||
assert (mt >= 0.0);
|
||||
|
||||
double sgn = 2.0*(n[0] == cell) - 1.0;
|
||||
dflux *= sgn;
|
||||
gflux *= sgn;
|
||||
|
||||
|
||||
double f1 = m[0] / mt;
|
||||
const double v1 = dflux + m[1]*gflux;
|
||||
|
||||
// Assemble residual contributions
|
||||
F[0] += dt * f1 * v1;
|
||||
F[1] += dt * mc * f1 * v1;
|
||||
|
||||
// Assemble Jacobian (J1 <-> cell, J2 <-> other)
|
||||
double *dFsds[2];
|
||||
double *dFsdc[2];
|
||||
double *dFcds[2];
|
||||
double *dFcdc[2];
|
||||
if (n[0] == cell) {
|
||||
dFsds[0] = &dFd1[0]; dFsds[1] = &dFd2[0];
|
||||
dFsdc[0] = &dFd1[1]; dFsdc[1] = &dFd2[1];
|
||||
dFcds[0] = &dFd1[2]; dFcds[1] = &dFd2[2];
|
||||
dFcdc[0] = &dFd1[3]; dFcdc[1] = &dFd2[3];
|
||||
// sign is positive
|
||||
dFd1[0] += sgn*dt * f1 * dpcflux[0] * m[1];
|
||||
dFd2[0] += sgn*dt * f1 * dpcflux[1] * m[1];
|
||||
dFd1[2] += sgn*dt * f1 * mc * dpcflux[0] * m[1];
|
||||
dFd2[2] += sgn*dt * f1 * mc * dpcflux[1] * m[1];
|
||||
// We assume that the capillary pressure is independent of the polymer concentration.
|
||||
// Hence, no more contributions.
|
||||
} else {
|
||||
dFsds[0] = &dFd2[0]; dFsds[1] = &dFd1[0];
|
||||
dFsdc[0] = &dFd2[1]; dFsdc[1] = &dFd1[1];
|
||||
dFcds[0] = &dFd2[2]; dFcds[1] = &dFd1[2];
|
||||
dFcdc[0] = &dFd2[3]; dFcdc[1] = &dFd1[3];
|
||||
// sign is negative
|
||||
dFd1[0] += sgn*dt * f1 * dpcflux[1] * m[1];
|
||||
dFd2[0] += sgn*dt * f1 * dpcflux[0] * m[1];
|
||||
dFd1[2] += sgn*dt * f1 * mc * dpcflux[1] * m[1];
|
||||
dFd2[2] += sgn*dt * f1 * mc * dpcflux[0] * m[1];
|
||||
// We assume that the capillary pressure is independent of the polymer concentration.
|
||||
// Hence, no more contributions.
|
||||
}
|
||||
|
||||
// dFs/dm_1 \cdot dm_1/ds
|
||||
*dFsds[ pix[0] ] += dt * (1 - f1) / mt * v1 * dmds[0];
|
||||
// dFc/dm_1 \cdot dm_1/ds
|
||||
*dFcds[ pix[0] ] += dt * (1 - f1) / mt * v1 * mc * dmds[0];
|
||||
|
||||
// dFs/dm_2 \cdot dm_2/ds
|
||||
*dFsds[ pix[1] ] -= dt * f1 / mt * v1 * dmds[1];
|
||||
*dFsds[ pix[1] ] += dt * f1 * gflux * dmds[1];
|
||||
// dFc/dm_2 \cdot dm_2/ds
|
||||
*dFcds[ pix[1] ] -= dt * f1 / mt * v1 * mc * dmds[1];
|
||||
*dFcds[ pix[1] ] += dt * f1 * gflux * mc * dmds[1];
|
||||
|
||||
// dFs/dm_1 \cdot dm_1/dc
|
||||
*dFsdc[ pix[0] ] += dt * (1 - f1) / mt * v1 * dmobwatdc;
|
||||
// dFc/dm_1 \cdot dm_1/dc
|
||||
*dFcdc[ pix[0] ] += dt * (1 - f1) / mt * v1 * mc * dmobwatdc;
|
||||
*dFcdc[ pix[0] ] += dt * f1 * v1 * dmcdc; // Polymer is only carried by water.
|
||||
}
|
||||
|
||||
template <class Grid>
|
||||
void
|
||||
accumulation(const Grid& g,
|
||||
const int cell,
|
||||
double* F, // Residual vector,
|
||||
double* dF // Jacobian, same convention as for fluxConnection.
|
||||
) const {
|
||||
(void) g;
|
||||
|
||||
const double pv = store_.porevol(cell);
|
||||
const double dps = fluid_.deadporespace();
|
||||
const double rhor = fluid_.rockdensity();
|
||||
const double poro = store_.porosity(cell);
|
||||
|
||||
F[0] += pv * store_.ds(cell);
|
||||
F[1] += pv * (1 - dps) * store_.dsc(cell) + rhor*(1 - poro)/poro*pv*store_.dcads(cell);
|
||||
dF[0] += pv;
|
||||
dF[1] += 0.;
|
||||
dF[2] += pv * (1 - dps) * store_.c(cell);
|
||||
dF[3] += pv * (1 - dps) * store_.sw(cell) + rhor*(1 - poro)/poro*pv*store_.dcadsdc(cell);
|
||||
}
|
||||
|
||||
template <class Grid ,
|
||||
class SourceTerms>
|
||||
void
|
||||
sourceTerms(const Grid& g ,
|
||||
const SourceTerms* src,
|
||||
const int i ,
|
||||
const double dt ,
|
||||
double* J ,
|
||||
double* F ) const {
|
||||
|
||||
(void) g;
|
||||
|
||||
double dflux = -src->flux[i]; // ->flux[] is rate of *inflow*
|
||||
|
||||
if (dflux < 0) {
|
||||
// src -> cell, affects residual only.
|
||||
*F += dt * dflux * src->saturation[2*i + 0];
|
||||
} else {
|
||||
// cell -> src
|
||||
const int cell = src->cell[i];
|
||||
const double* m = store_.mob (cell);
|
||||
const double* dm = store_.dmobds(cell);
|
||||
|
||||
const double mt = m[0] + m[1];
|
||||
|
||||
assert (! ((m[0] < 0) || (m[1] < 0)));
|
||||
assert (mt > 0);
|
||||
|
||||
const double f = m[0] / mt;
|
||||
const double df = ((1 - f)*dm[0] - f*dm[1]) / mt;
|
||||
|
||||
*F += dt * dflux * f;
|
||||
*J += dt * dflux * df;
|
||||
}
|
||||
}
|
||||
template <class Grid>
|
||||
void
|
||||
initGravityTrans(const Grid& g ,
|
||||
const std::vector<double> & htrans) {
|
||||
|
||||
assert (htrans.size() ==
|
||||
static_cast<std::vector<double>::size_type>(g.cell_facepos[ g.number_of_cells ]));
|
||||
|
||||
for (int f = 0; f < g.number_of_faces; ++f) {
|
||||
store_.trans(f) = 0.0;
|
||||
}
|
||||
|
||||
for (int c = 0, i = 0; c < g.number_of_cells; ++c) {
|
||||
for (; i < g.cell_facepos[c + 1]; ++i) {
|
||||
int f = g.cell_faces[i];
|
||||
|
||||
assert (htrans[i] > 0.0);
|
||||
|
||||
store_.trans(f) += 1.0 / htrans[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int f = 0; f < g.number_of_faces; ++f) {
|
||||
store_.trans(f) = 1.0 / store_.trans(f);
|
||||
}
|
||||
|
||||
if (gravity_) {
|
||||
this->computeStaticGravity(g);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Newton control
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
template <class ReservoirState,
|
||||
class Grid ,
|
||||
class JacobianSystem>
|
||||
void
|
||||
initStep(const ReservoirState& state,
|
||||
const Grid& g ,
|
||||
JacobianSystem& sys ) {
|
||||
|
||||
(void) state; // Suppress 'unused' warning.
|
||||
|
||||
typename JacobianSystem::vector_type& x =
|
||||
sys.vector().writableSolution();
|
||||
|
||||
assert (x.size() == (::std::size_t) (2*g.number_of_cells));
|
||||
|
||||
if (init_step_use_previous_sol_) {
|
||||
std::fill(x.begin(), x.end(), 0.0);
|
||||
} else {
|
||||
std::fill(x.begin(), x.end(), 0.0);
|
||||
const std::vector<double>& s = state.saturation();
|
||||
const std::vector<double>& c = state.concentration();
|
||||
for (int cell = 0, ncell = g.number_of_cells; cell < ncell; ++cell) {
|
||||
// Impose s=0.5 at next time level as an NR initial value.
|
||||
x[2*cell + 0] = 0.5 - s[2*cell + 0];
|
||||
x[2*cell + 1] = 1e-5 - c[cell];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ReservoirState,
|
||||
class Grid ,
|
||||
class JacobianSystem>
|
||||
bool
|
||||
initIteration(const ReservoirState& state,
|
||||
const Grid& g ,
|
||||
JacobianSystem& sys) {
|
||||
|
||||
double s[2];
|
||||
double mob[2];
|
||||
double dmobds[4];
|
||||
double dmobwatdc;
|
||||
double c, cmax;
|
||||
double mc, dmcdc;
|
||||
double pc, dpc;
|
||||
|
||||
const typename JacobianSystem::vector_type& x =
|
||||
sys.vector().solution();
|
||||
const ::std::vector<double>& sat = state.saturation();
|
||||
const ::std::vector<double>& cpoly = state.concentration();
|
||||
const ::std::vector<double>& cmaxpoly = state.maxconcentration();
|
||||
|
||||
bool in_range = true;
|
||||
for (int cell = 0; cell < g.number_of_cells; ++cell) {
|
||||
// Store wat-sat, sat-change, cpoly, (sat * cpoly)-change for accumulation().
|
||||
store_.ds(cell) = x[2*cell + 0];
|
||||
s[0] = sat[cell*2 + 0] + x[2*cell + 0];
|
||||
c = cpoly[cell] + x[2*cell + 1];
|
||||
store_.sw(cell) = s[0];
|
||||
store_.c(cell) = c;
|
||||
cmax = std::max(c, cmaxpoly[cell]);
|
||||
store_.cmax(cell) = cmax;
|
||||
store_.dsc(cell) = s[0]*c - sat[cell*2 + 0]*cpoly[cell];
|
||||
double dcadsdc;
|
||||
double cads;
|
||||
fluid_.adsorption(cpoly[cell], cmaxpoly[cell], cads, dcadsdc);
|
||||
store_.dcads(cell) = -cads;
|
||||
fluid_.adsorption(c, cmax, cads, dcadsdc);
|
||||
store_.dcads(cell) += cads;
|
||||
store_.dcadsdc(cell) = dcadsdc;
|
||||
double s_min = fluid_.s_min(cell);
|
||||
double s_max = fluid_.s_max(cell);
|
||||
|
||||
if ( s[0] < (s_min - sat_tol_) || s[0] > (s_max + sat_tol_) ) {
|
||||
// if (s[0] < s_min){
|
||||
// std::cout << "Warning: s out of range, s-s_min = " << s_min-s[0] << std::endl;
|
||||
// }
|
||||
// if (s[0] > s_max){
|
||||
// std::cout << "Warning: s out of range, s-s_max = " << s[0]-s_max << std::endl;
|
||||
// }
|
||||
in_range = false; //line search fails
|
||||
}
|
||||
s[0] = std::max(s_min, s[0]);
|
||||
s[0] = std::min(s_max, s[0]);
|
||||
s[1] = 1 - s[0];
|
||||
|
||||
fluid_.mobility(cell, s, c, cmax, mob, dmobds, dmobwatdc);
|
||||
fluid_.computeMc(c, mc, dmcdc);
|
||||
fluid_.pc(cell, s, pc, dpc);
|
||||
|
||||
store_.mob (cell)[0] = mob [0];
|
||||
store_.mob (cell)[1] = mob [1];
|
||||
store_.dmobds(cell)[0] = dmobds[0*2 + 0];
|
||||
store_.dmobds(cell)[1] = -dmobds[1*2 + 1];
|
||||
store_.dmobwatdc(cell) = dmobwatdc;
|
||||
store_.mc(cell) = mc;
|
||||
store_.dmcdc(cell) = dmcdc;
|
||||
store_.pc(cell) = pc;
|
||||
store_.dpc(cell) = dpc;
|
||||
}
|
||||
if (!in_range) {
|
||||
std::cout << "Warning: initIteration() - s was clamped in some cells.\n";
|
||||
}
|
||||
return in_range;
|
||||
}
|
||||
|
||||
template <class ReservoirState,
|
||||
class Grid ,
|
||||
class NewtonIterate >
|
||||
void
|
||||
finishIteration(const ReservoirState& state,
|
||||
const Grid& g ,
|
||||
NewtonIterate& it ) {
|
||||
// Nothing to do at end of iteration in this model.
|
||||
(void) state; (void) g; (void) it;
|
||||
typedef typename NewtonIterate::vector_type vector_t;
|
||||
}
|
||||
|
||||
template <class Grid ,
|
||||
class SolutionVector,
|
||||
class ReservoirState>
|
||||
void
|
||||
finishStep(const Grid& g ,
|
||||
const SolutionVector& x ,
|
||||
ReservoirState& state) {
|
||||
|
||||
double *s = &state.saturation()[0*2 + 0];
|
||||
double *c = &state.concentration()[0*1 + 0];
|
||||
double *cmax = &state.maxconcentration()[0*1 + 0];
|
||||
|
||||
for (int cell = 0; cell < g.number_of_cells; ++cell, s += 2, c += 1, cmax +=1) {
|
||||
s[0] += x[2*cell + 0];
|
||||
c[0] += x[2*cell + 1];
|
||||
cmax[0] = std::max(c[0], cmax[0]);
|
||||
double s_min = fluid_.s_min(cell);
|
||||
double s_max = fluid_.s_max(cell);
|
||||
assert(s[0] >= s_min - sat_tol_);
|
||||
assert(s[0] <= s_max + sat_tol_);
|
||||
s[0] = std::max(s_min, s[0]);
|
||||
s[0] = std::min(s_max, s[0]);
|
||||
s[1] = 1.0 - s[0];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
upwindMobility(const double dflux,
|
||||
const double gflux,
|
||||
const int* n ,
|
||||
int* pix ,
|
||||
double* m ,
|
||||
double* dmds ,
|
||||
double& dmobwatdc ,
|
||||
double& mc,
|
||||
double& dmcdc) const {
|
||||
bool equal_sign = ( (! (dflux < 0)) && (! (gflux < 0)) ) ||
|
||||
( (! (dflux > 0)) && (! (gflux > 0)) );
|
||||
|
||||
if (equal_sign) {
|
||||
|
||||
if (! (dflux < 0) && ! (gflux < 0)) { pix[0] = 0; }
|
||||
else { pix[0] = 1; }
|
||||
|
||||
m[0] = store_.mob(n[ pix[0] ]) [ 0 ];
|
||||
mc = store_.mc(n[ pix[0] ]);
|
||||
|
||||
if (! (dflux - m[0]*gflux < 0)) { pix[1] = 0; }
|
||||
else { pix[1] = 1; }
|
||||
|
||||
m[1] = store_.mob(n[ pix[1] ]) [ 1 ];
|
||||
|
||||
} else {
|
||||
|
||||
if (! (dflux < 0) && ! (gflux > 0)) { pix[1] = 0; }
|
||||
else { pix[1] = 1; }
|
||||
|
||||
m[1] = store_.mob(n[ pix[1] ]) [ 1 ];
|
||||
|
||||
if (dflux + m[1]*gflux > 0) { pix[0] = 0; }
|
||||
else { pix[0] = 1; }
|
||||
|
||||
m[0] = store_.mob(n[ pix[0] ]) [ 0 ];
|
||||
mc = store_.mc(n[ pix[0] ]);
|
||||
}
|
||||
|
||||
dmds[0] = store_.dmobds(n[ pix[0] ]) [ 0 ];
|
||||
dmds[1] = store_.dmobds(n[ pix[1] ]) [ 1 ];
|
||||
dmobwatdc = store_.dmobwatdc(n[ pix[0] ]);
|
||||
dmcdc = store_.dmcdc(n[ pix[0] ]);
|
||||
}
|
||||
|
||||
template <class Grid>
|
||||
void
|
||||
computeStaticGravity(const Grid& g) {
|
||||
|
||||
const int d = g.dimensions;
|
||||
|
||||
for (int c = 0, i = 0; c < g.number_of_cells; ++c) {
|
||||
const double* cc = g.cell_centroids + (c * d);
|
||||
|
||||
for (; i < g.cell_facepos[c + 1]; ++i) {
|
||||
const int f = g.cell_faces[i];
|
||||
const double* fc = g.face_centroids + (f * d);
|
||||
|
||||
double dg = 0.0;
|
||||
for (int j = 0; j < d; ++j) {
|
||||
dg += gravity_[j] * (fc[j] - cc[j]);
|
||||
}
|
||||
|
||||
store_.dg(i) = store_.trans(f) * dg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
gravityFlux(const int f) const {
|
||||
double gflux;
|
||||
|
||||
if (gravity_) {
|
||||
int i1 = f2hf_[2*f + 0];
|
||||
int i2 = f2hf_[2*f + 1];
|
||||
|
||||
assert ((i1 >= 0) && (i2 >= 0));
|
||||
|
||||
gflux = store_.dg(i1) - store_.dg(i2);
|
||||
gflux *= store_.drho();
|
||||
} else {
|
||||
gflux = 0.0;
|
||||
}
|
||||
|
||||
return gflux;
|
||||
}
|
||||
void
|
||||
capFlux(const int f,const int* n,double& pcflux, double* dpcflux) const {
|
||||
//double capflux;
|
||||
int i1 = n[0];
|
||||
int i2 = n[1];
|
||||
assert ((i1 >= 0) && (i2 >= 0));
|
||||
//double sgn=-1.0;
|
||||
pcflux = store_.trans(f)*(store_.pc(i2) - store_.pc(i1));
|
||||
dpcflux[0] = -store_.trans(f)*store_.dpc(i1);
|
||||
dpcflux[1] = store_.trans(f)*store_.dpc(i2);
|
||||
}
|
||||
|
||||
TwophaseFluidPolymer fluid_ ;
|
||||
const double* gravity_;
|
||||
std::vector<int> f2hf_ ;
|
||||
polymer_reorder::ModelParameterStorage store_ ;
|
||||
bool init_step_use_previous_sol_;
|
||||
double sat_tol_;
|
||||
};
|
||||
}
|
||||
#endif /* OPM_SINGLEPOINTUPWINDTWOPHASE_HPP_HEADER */
|
1533
opm/polymer/TransportSolverTwophaseCompressiblePolymer.cpp
Normal file
1533
opm/polymer/TransportSolverTwophaseCompressiblePolymer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
236
opm/polymer/TransportSolverTwophaseCompressiblePolymer.hpp
Normal file
236
opm/polymer/TransportSolverTwophaseCompressiblePolymer.hpp
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_TRANSPORTSOLVERTWOPHASECOMPRESSIBLEPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_TRANSPORTSOLVERTWOPHASECOMPRESSIBLEPOLYMER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
#include <opm/core/transport/reorder/ReorderSolverInterface.hpp>
|
||||
#include <opm/core/utility/linearInterpolation.hpp>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
namespace {
|
||||
class ResSOnCurve;
|
||||
class ResCOnCurve;
|
||||
}
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class BlackoilPropertiesInterface;
|
||||
|
||||
/// Implements a reordering transport solver for incompressible two-phase flow
|
||||
/// with polymer in the water phase.
|
||||
/// \TODO Include permeability reduction effect.
|
||||
class TransportSolverTwophaseCompressiblePolymer : public ReorderSolverInterface
|
||||
{
|
||||
public:
|
||||
|
||||
enum SingleCellMethod { Bracketing, Newton, NewtonC, Gradient};
|
||||
enum GradientMethod { Analytic, FinDif }; // Analytic is chosen (hard-coded)
|
||||
|
||||
/// Construct solver.
|
||||
/// \param[in] grid A 2d or 3d grid.
|
||||
/// \param[in] props Rock and fluid properties.
|
||||
/// \param[in] polyprops Polymer properties.
|
||||
/// \param[in] rock_comp Rock compressibility properties
|
||||
/// \param[in] method Bracketing: solve for c in outer loop, s in inner loop,
|
||||
/// each solve being bracketed for robustness.
|
||||
/// Newton: solve simultaneously for c and s with Newton's method.
|
||||
/// (using gradient variant and bracketing as fallbacks).
|
||||
/// \param[in] tol Tolerance used in the solver.
|
||||
/// \param[in] maxit Maximum number of non-linear iterations used.
|
||||
TransportSolverTwophaseCompressiblePolymer(const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const PolymerProperties& polyprops,
|
||||
const SingleCellMethod method,
|
||||
const double tol,
|
||||
const int maxit);
|
||||
|
||||
/// Set the preferred method, Bracketing or Newton.
|
||||
void setPreferredMethod(SingleCellMethod method);
|
||||
|
||||
/// Solve for saturation, concentration and cmax at next timestep.
|
||||
/// Using implicit Euler scheme, reordered.
|
||||
/// \param[in] darcyflux Array of signed face fluxes.
|
||||
/// \param[in] initial_pressure Array with pressure at start of timestep.
|
||||
/// \param[in] pressure Array with pressure.
|
||||
/// \param[in] temperature Array with temperature.
|
||||
/// \param[in] porevolume0 Array with pore volume at start of timestep.
|
||||
/// \param[in] porevolume Array with pore volume.
|
||||
/// \param[in] source Transport source term, to be interpreted by sign:
|
||||
/// (+) Inflow, value is first phase flow (water)
|
||||
/// per second, in *surface* volumes (unlike the
|
||||
/// incompressible version).
|
||||
/// (-) Outflow, value is total flow of all phases
|
||||
/// per second, in reservoir volumes.
|
||||
/// \param[in] polymer_inflow_c Array of inflow polymer concentrations per cell.
|
||||
/// \param[in] dt Time step.
|
||||
/// \param[in, out] saturation Phase saturations.
|
||||
/// \param[in, out] surfacevol Surface volumes.
|
||||
/// \param[in, out] concentration Polymer concentration.
|
||||
/// \param[in, out] cmax Highest concentration that has occured in a given cell.
|
||||
void solve(const double* darcyflux,
|
||||
const std::vector<double>& initial_pressure,
|
||||
const std::vector<double>& pressure,
|
||||
const std::vector<double>& temperature,
|
||||
const double* porevolume0,
|
||||
const double* porevolume,
|
||||
const double* source,
|
||||
const double* polymer_inflow_c,
|
||||
const double dt,
|
||||
std::vector<double>& saturation,
|
||||
std::vector<double>& surfacevol,
|
||||
std::vector<double>& concentration,
|
||||
std::vector<double>& cmax);
|
||||
|
||||
/// Initialise quantities needed by gravity solver.
|
||||
/// \param[in] grav Gravity vector
|
||||
void initGravity(const double* grav);
|
||||
|
||||
/// Solve for gravity segregation.
|
||||
/// This uses a column-wise nonlinear Gauss-Seidel approach.
|
||||
/// It assumes that the input columns contain cells in a single
|
||||
/// vertical stack, that do not interact with other columns (for
|
||||
/// gravity segregation.
|
||||
/// \param[in] columns Vector of cell-columns.
|
||||
/// \param[in] dt Time step.
|
||||
/// \param[in, out] saturation Phase saturations.
|
||||
/// \param[in, out] surfacevol Surface volumes.
|
||||
/// \param[in, out] concentration Polymer concentration.
|
||||
/// \param[in, out] cmax Highest concentration that has occured in a given cell.
|
||||
void solveGravity(const std::vector<std::vector<int> >& columns,
|
||||
const double dt,
|
||||
std::vector<double>& saturation,
|
||||
std::vector<double>& surfacevol,
|
||||
std::vector<double>& concentration,
|
||||
std::vector<double>& cmax);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
const UnstructuredGrid& grid_;
|
||||
const BlackoilPropertiesInterface& props_;
|
||||
const PolymerProperties& polyprops_;
|
||||
const double* darcyflux_; // one flux per grid face
|
||||
const double* porevolume0_; // one volume per cell
|
||||
const double* porevolume_; // one volume per cell
|
||||
const double* source_; // one source per cell
|
||||
const double* polymer_inflow_c_;
|
||||
double dt_;
|
||||
double tol_;
|
||||
double maxit_;
|
||||
SingleCellMethod method_;
|
||||
double adhoc_safety_;
|
||||
|
||||
std::vector<double> saturation_; // one per cell, only water saturation!
|
||||
std::vector<int> allcells_;
|
||||
double* concentration_;
|
||||
double* cmax_;
|
||||
std::vector<double> fractionalflow_; // one per cell
|
||||
std::vector<double> mc_; // one per cell
|
||||
std::vector<double> visc_; // viscosity (without polymer, for given pressure)
|
||||
std::vector<double> A_;
|
||||
std::vector<double> A0_;
|
||||
std::vector<double> smin_;
|
||||
std::vector<double> smax_;
|
||||
|
||||
// For gravity segregation.
|
||||
const double* gravity_;
|
||||
std::vector<double> trans_;
|
||||
std::vector<double> density_;
|
||||
std::vector<double> gravflux_;
|
||||
std::vector<double> mob_;
|
||||
std::vector<double> cmax0_;
|
||||
|
||||
// For gravity segregation, column variables
|
||||
std::vector<double> s0_;
|
||||
std::vector<double> c0_;
|
||||
|
||||
// Storing the upwind and downwind graphs for experiments.
|
||||
std::vector<int> ia_upw_;
|
||||
std::vector<int> ja_upw_;
|
||||
std::vector<int> ia_downw_;
|
||||
std::vector<int> ja_downw_;
|
||||
|
||||
struct ResidualC;
|
||||
struct ResidualS;
|
||||
|
||||
class ResidualCGrav;
|
||||
class ResidualSGrav;
|
||||
|
||||
class ResidualEquation;
|
||||
class ResSOnCurve;
|
||||
class ResCOnCurve;
|
||||
|
||||
friend class TransportSolverTwophaseCompressiblePolymer::ResidualEquation;
|
||||
friend class TransportSolverTwophaseCompressiblePolymer::ResSOnCurve;
|
||||
friend class TransportSolverTwophaseCompressiblePolymer::ResCOnCurve;
|
||||
|
||||
|
||||
virtual void solveSingleCell(const int cell);
|
||||
virtual void solveMultiCell(const int num_cells, const int* cells);
|
||||
void solveSingleCellBracketing(int cell);
|
||||
void solveSingleCellNewton(int cell, bool use_sc, bool use_explicit_step = false);
|
||||
void solveSingleCellGradient(int cell);
|
||||
void solveSingleCellGravity(const std::vector<int>& cells,
|
||||
const int pos,
|
||||
const double* gravflux);
|
||||
int solveGravityColumn(const std::vector<int>& cells);
|
||||
|
||||
void initGravityDynamic();
|
||||
|
||||
void fracFlow(double s, double c, double cmax, int cell, double& ff) const;
|
||||
void fracFlowWithDer(double s, double c, double cmax, int cell, double& ff,
|
||||
double* dff_dsdc) const;
|
||||
void fracFlowBoth(double s, double c, double cmax, int cell, double& ff,
|
||||
double* dff_dsdc, bool if_with_der) const;
|
||||
void computeMc(double c, double& mc) const;
|
||||
void computeMcWithDer(double c, double& mc, double& dmc_dc) const;
|
||||
void mobility(double s, double c, int cell, double* mob) const;
|
||||
void scToc(const double* x, double* x_c) const;
|
||||
#ifdef PROFILING
|
||||
class Newton_Iter {
|
||||
public:
|
||||
bool res_s;
|
||||
int cell;
|
||||
double s;
|
||||
double c;
|
||||
|
||||
Newton_Iter(bool res_s_val, int cell_val, double s_val, double c_val) {
|
||||
res_s = res_s_val;
|
||||
cell = cell_val;
|
||||
s = s_val;
|
||||
c = c_val;
|
||||
}
|
||||
};
|
||||
|
||||
std::list<Newton_Iter> res_counts;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_TRANSPORTSOLVERTWOPHASECOMPRESSIBLEPOLYMER_HEADER_INCLUDED
|
1555
opm/polymer/TransportSolverTwophasePolymer.cpp
Normal file
1555
opm/polymer/TransportSolverTwophasePolymer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
194
opm/polymer/TransportSolverTwophasePolymer.hpp
Normal file
194
opm/polymer/TransportSolverTwophasePolymer.hpp
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_TRANSPORTSOLVERTWOPHASEPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_TRANSPORTSOLVERTWOPHASEPOLYMER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
#include <opm/core/transport/reorder/ReorderSolverInterface.hpp>
|
||||
#include <opm/core/utility/linearInterpolation.hpp>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class IncompPropertiesInterface;
|
||||
|
||||
/// Implements a reordering transport solver for incompressible two-phase flow
|
||||
/// with polymer in the water phase.
|
||||
/// \TODO Include permeability reduction effect.
|
||||
class TransportSolverTwophasePolymer : public ReorderSolverInterface
|
||||
{
|
||||
public:
|
||||
|
||||
enum SingleCellMethod { Bracketing, Newton, Gradient, NewtonSimpleSC, NewtonSimpleC};
|
||||
enum GradientMethod { Analytic, FinDif }; // Analytic is chosen (hard-coded)
|
||||
|
||||
/// Construct solver.
|
||||
/// \param[in] grid A 2d or 3d grid.
|
||||
/// \param[in] props Rock and fluid properties.
|
||||
/// \param[in] polyprops Polymer properties.
|
||||
/// \param[in] method Bracketing: solve for c in outer loop, s in inner loop,
|
||||
/// each solve being bracketed for robustness.
|
||||
/// Newton: solve simultaneously for c and s with Newton's method.
|
||||
/// (using gradient variant and bracketing as fallbacks).
|
||||
/// \param[in] tol Tolerance used in the solver.
|
||||
/// \param[in] maxit Maximum number of non-linear iterations used.
|
||||
TransportSolverTwophasePolymer(const UnstructuredGrid& grid,
|
||||
const IncompPropertiesInterface& props,
|
||||
const PolymerProperties& polyprops,
|
||||
const SingleCellMethod method,
|
||||
const double tol,
|
||||
const int maxit);
|
||||
|
||||
/// Set the preferred method, Bracketing or Newton.
|
||||
void setPreferredMethod(SingleCellMethod method);
|
||||
|
||||
/// Solve for saturation, concentration and cmax at next timestep.
|
||||
/// Using implicit Euler scheme, reordered.
|
||||
/// \param[in] darcyflux Array of signed face fluxes.
|
||||
/// \param[in] porevolume Array of pore volumes.
|
||||
/// \param[in] source Transport source term, to be interpreted by sign:
|
||||
/// (+) Inflow, value is first phase flow (water)
|
||||
/// per second, in reservoir volumes.
|
||||
/// (-) Outflow, value is total flow of all phases
|
||||
/// per second, in reservoir volumes.
|
||||
/// \param[in] polymer_inflow_c Array of inflow polymer concentrations per cell.
|
||||
/// \param[in] dt Time step.
|
||||
/// \param[in, out] saturation Phase saturations.
|
||||
/// \param[in, out] concentration Polymer concentration.
|
||||
/// \param[in, out] cmax Highest concentration that has occured in a given cell.
|
||||
void solve(const double* darcyflux,
|
||||
const double* porevolume,
|
||||
const double* source,
|
||||
const double* polymer_inflow_c,
|
||||
const double dt,
|
||||
std::vector<double>& saturation,
|
||||
std::vector<double>& concentration,
|
||||
std::vector<double>& cmax);
|
||||
|
||||
/// Solve for gravity segregation.
|
||||
/// This uses a column-wise nonlinear Gauss-Seidel approach.
|
||||
/// It assumes that the input columns contain cells in a single
|
||||
/// vertical stack, that do not interact with other columns (for
|
||||
/// gravity segregation.
|
||||
/// \param[in] columns Vector of cell-columns.
|
||||
/// \param[in] porevolume Array of pore volumes.
|
||||
/// \param[in] dt Time step.
|
||||
/// \param[in, out] saturation Phase saturations.
|
||||
/// \param[in, out] concentration Polymer concentration.
|
||||
/// \param[in, out] cmax Highest concentration that has occured in a given cell.
|
||||
void solveGravity(const std::vector<std::vector<int> >& columns,
|
||||
const double* porevolume,
|
||||
const double dt,
|
||||
std::vector<double>& saturation,
|
||||
std::vector<double>& concentration,
|
||||
std::vector<double>& cmax);
|
||||
|
||||
public: // But should be made private...
|
||||
virtual void solveSingleCell(const int cell);
|
||||
virtual void solveMultiCell(const int num_cells, const int* cells);
|
||||
void solveSingleCellBracketing(int cell);
|
||||
void solveSingleCellNewton(int cell);
|
||||
void solveSingleCellGradient(int cell);
|
||||
void solveSingleCellNewtonSimple(int cell,bool use_sc);
|
||||
class ResidualEquation;
|
||||
|
||||
void initGravity(const double* grav);
|
||||
void solveSingleCellGravity(const std::vector<int>& cells,
|
||||
const int pos,
|
||||
const double* gravflux);
|
||||
int solveGravityColumn(const std::vector<int>& cells);
|
||||
void scToc(const double* x, double* x_c) const;
|
||||
|
||||
#ifdef PROFILING
|
||||
class Newton_Iter {
|
||||
public:
|
||||
bool res_s;
|
||||
int cell;
|
||||
double s;
|
||||
double c;
|
||||
|
||||
Newton_Iter(bool res_s_val, int cell_val, double s_val, double c_val) {
|
||||
res_s = res_s_val;
|
||||
cell = cell_val;
|
||||
s = s_val;
|
||||
c = c_val;
|
||||
}
|
||||
};
|
||||
|
||||
std::list<Newton_Iter> res_counts;
|
||||
#endif
|
||||
|
||||
|
||||
private:
|
||||
const UnstructuredGrid& grid_;
|
||||
const double* porosity_;
|
||||
const double* porevolume_; // one volume per cell
|
||||
const IncompPropertiesInterface& props_;
|
||||
const PolymerProperties& polyprops_;
|
||||
std::vector<double> smin_;
|
||||
std::vector<double> smax_;
|
||||
double tol_;
|
||||
double maxit_;
|
||||
|
||||
const double* darcyflux_; // one flux per grid face
|
||||
const double* source_; // one source per cell
|
||||
const double* polymer_inflow_c_;
|
||||
double dt_;
|
||||
std::vector<double> saturation_; // one per cell, only water saturation!
|
||||
double* concentration_;
|
||||
double* cmax_;
|
||||
std::vector<double> fractionalflow_; // one per cell
|
||||
std::vector<double> mc_; // one per cell
|
||||
const double* visc_;
|
||||
SingleCellMethod method_;
|
||||
double adhoc_safety_;
|
||||
|
||||
// For gravity segregation.
|
||||
std::vector<double> gravflux_;
|
||||
std::vector<double> mob_;
|
||||
std::vector<double> cmax0_;
|
||||
// For gravity segregation, column variables
|
||||
std::vector<double> s0_;
|
||||
std::vector<double> c0_;
|
||||
|
||||
struct ResidualC;
|
||||
struct ResidualS;
|
||||
|
||||
class ResidualCGrav;
|
||||
class ResidualSGrav;
|
||||
|
||||
|
||||
void fracFlow(double s, double c, double cmax, int cell, double& ff) const;
|
||||
void fracFlowWithDer(double s, double c, double cmax, int cell, double& ff,
|
||||
double* dff_dsdc) const;
|
||||
void fracFlowBoth(double s, double c, double cmax, int cell, double& ff,
|
||||
double* dff_dsdc, bool if_with_der) const;
|
||||
void computeMc(double c, double& mc) const;
|
||||
void computeMcWithDer(double c, double& mc, double& dmc_dc) const;
|
||||
void mobility(double s, double c, int cell, double* mob) const;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_TRANSPORTSOLVERTWOPHASEPOLYMER_HEADER_INCLUDED
|
297
opm/polymer/fullyimplicit/BlackoilPolymerModel.hpp
Normal file
297
opm/polymer/fullyimplicit/BlackoilPolymerModel.hpp
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
Copyright 2013, 2015 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_BLACKOILPOLYMERMODEL_HEADER_INCLUDED
|
||||
#define OPM_BLACKOILPOLYMERMODEL_HEADER_INCLUDED
|
||||
|
||||
#include <opm/autodiff/BlackoilModelBase.hpp>
|
||||
#include <opm/autodiff/BlackoilModelParameters.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
#include <opm/polymer/fullyimplicit/PolymerPropsAd.hpp>
|
||||
#include <opm/polymer/PolymerBlackoilState.hpp>
|
||||
#include <opm/polymer/fullyimplicit/WellStateFullyImplicitBlackoilPolymer.hpp>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
/// A model implementation for three-phase black oil with polymer.
|
||||
///
|
||||
/// The simulator is capable of handling three-phase problems
|
||||
/// where gas can be dissolved in oil and vice versa, with polymer
|
||||
/// in the water phase. 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 Grid>
|
||||
class BlackoilPolymerModel : public BlackoilModelBase<Grid, BlackoilPolymerModel<Grid> >
|
||||
{
|
||||
public:
|
||||
|
||||
// --------- Types and enums ---------
|
||||
|
||||
typedef BlackoilModelBase<Grid, BlackoilPolymerModel<Grid> > Base;
|
||||
typedef typename Base::ReservoirState ReservoirState;
|
||||
typedef typename Base::WellState WellState;
|
||||
// The next line requires C++11 support available in g++ 4.7.
|
||||
// friend Base;
|
||||
friend class BlackoilModelBase<Grid, BlackoilPolymerModel<Grid> >;
|
||||
|
||||
/// 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] linsolver linear solver
|
||||
/// \param[in] has_disgas turn on dissolved gas
|
||||
/// \param[in] has_vapoil turn on vaporized oil feature
|
||||
/// \param[in] has_polymer turn on polymer feature
|
||||
/// \param[in] has_plyshlog true when PLYSHLOG keyword available
|
||||
/// \param[in] has_shrate true when PLYSHLOG keyword available
|
||||
/// \param[in] wells_rep_radius representative radius of well perforations during shear effects calculation
|
||||
/// \param[in] wells_perf_length perforation length for well perforations
|
||||
/// \param[in] wells_bore_diameter wellbore diameters for well performations
|
||||
/// \param[in] terminal_output request output to cout/cerr
|
||||
BlackoilPolymerModel(const typename Base::ModelParameters& 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,
|
||||
EclipseStateConstPtr eclipse_state,
|
||||
const bool has_disgas,
|
||||
const bool has_vapoil,
|
||||
const bool has_polymer,
|
||||
const bool has_plyshlog,
|
||||
const bool has_shrate,
|
||||
const std::vector<double>& wells_rep_radius,
|
||||
const std::vector<double>& wells_perf_length,
|
||||
const std::vector<double>& wells_bore_diameter,
|
||||
const bool terminal_output);
|
||||
|
||||
/// 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);
|
||||
|
||||
/// Called once after 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 afterStep(const double dt,
|
||||
ReservoirState& reservoir_state,
|
||||
WellState& well_state);
|
||||
|
||||
/// Apply an update to the primary variables, chopped if appropriate.
|
||||
/// \param[in] dx updates to apply to primary variables
|
||||
/// \param[in, out] reservoir_state reservoir state variables
|
||||
/// \param[in, out] well_state well state variables
|
||||
void updateState(const V& dx,
|
||||
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);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// --------- Types and enums ---------
|
||||
|
||||
typedef typename Base::SolutionState SolutionState;
|
||||
typedef typename Base::DataBlock DataBlock;
|
||||
enum { Concentration = CanonicalVariablePositions::Next };
|
||||
|
||||
// --------- Data members ---------
|
||||
|
||||
const PolymerPropsAd& polymer_props_ad_;
|
||||
const bool has_polymer_;
|
||||
const bool has_plyshlog_;
|
||||
const bool has_shrate_;
|
||||
const int poly_pos_;
|
||||
V cmax_;
|
||||
|
||||
// representative radius and perforation length of well perforations
|
||||
// to be used in shear-thinning computation.
|
||||
std::vector<double> wells_rep_radius_;
|
||||
std::vector<double> wells_perf_length_;
|
||||
// wellbore diameters
|
||||
std::vector<double> wells_bore_diameter_;
|
||||
|
||||
// shear-thinning factor for cell faces
|
||||
std::vector<double> shear_mult_faces_;
|
||||
// shear-thinning factor for well perforations
|
||||
std::vector<double> shear_mult_wells_;
|
||||
|
||||
// Need to declare Base members we want to use here.
|
||||
using Base::grid_;
|
||||
using Base::fluid_;
|
||||
using Base::geo_;
|
||||
using Base::rock_comp_props_;
|
||||
using Base::wells_;
|
||||
using Base::linsolver_;
|
||||
using Base::active_;
|
||||
using Base::canph_;
|
||||
using Base::cells_;
|
||||
using Base::ops_;
|
||||
using Base::wops_;
|
||||
using Base::has_disgas_;
|
||||
using Base::has_vapoil_;
|
||||
using Base::param_;
|
||||
using Base::use_threshold_pressure_;
|
||||
using Base::threshold_pressures_by_interior_face_;
|
||||
using Base::rq_;
|
||||
using Base::phaseCondition_;
|
||||
using Base::well_perforation_pressure_diffs_;
|
||||
using Base::residual_;
|
||||
using Base::terminal_output_;
|
||||
using Base::primalVariable_;
|
||||
using Base::pvdt_;
|
||||
|
||||
// --------- Protected methods ---------
|
||||
|
||||
// Need to declare Base members we want to use here.
|
||||
using Base::wellsActive;
|
||||
using Base::wells;
|
||||
using Base::variableState;
|
||||
using Base::computePressures;
|
||||
using Base::computeGasPressure;
|
||||
using Base::applyThresholdPressures;
|
||||
using Base::fluidViscosity;
|
||||
using Base::fluidReciprocFVF;
|
||||
using Base::fluidDensity;
|
||||
using Base::fluidRsSat;
|
||||
using Base::fluidRvSat;
|
||||
using Base::poroMult;
|
||||
using Base::transMult;
|
||||
using Base::updatePrimalVariableFromState;
|
||||
using Base::updatePhaseCondFromPrimalVariable;
|
||||
using Base::dpMaxRel;
|
||||
using Base::dsMax;
|
||||
using Base::drMaxRel;
|
||||
using Base::maxResidualAllowed;
|
||||
|
||||
using Base::updateWellControls;
|
||||
using Base::computeWellConnectionPressures;
|
||||
using Base::addWellControlEq;
|
||||
using Base::computeRelPerm;
|
||||
|
||||
|
||||
void
|
||||
makeConstantState(SolutionState& state) const;
|
||||
|
||||
std::vector<V>
|
||||
variableStateInitials(const ReservoirState& x,
|
||||
const WellState& xw) const;
|
||||
|
||||
std::vector<int>
|
||||
variableStateIndices() const;
|
||||
|
||||
SolutionState
|
||||
variableStateExtractVars(const ReservoirState& x,
|
||||
const std::vector<int>& indices,
|
||||
std::vector<ADB>& vars) const;
|
||||
|
||||
void
|
||||
computeAccum(const SolutionState& state,
|
||||
const int aix );
|
||||
|
||||
void
|
||||
assembleMassBalanceEq(const SolutionState& state);
|
||||
|
||||
void
|
||||
addWellContributionToMassBalanceEq(const std::vector<ADB>& cq_s,
|
||||
const SolutionState& state,
|
||||
WellState& xw);
|
||||
|
||||
void
|
||||
computeMassFlux(const int actph ,
|
||||
const V& transi,
|
||||
const ADB& kr ,
|
||||
const ADB& p ,
|
||||
const SolutionState& state );
|
||||
|
||||
void
|
||||
computeCmax(ReservoirState& state);
|
||||
|
||||
ADB
|
||||
computeMc(const SolutionState& state) const;
|
||||
|
||||
const std::vector<PhasePresence>
|
||||
phaseCondition() const {return this->phaseCondition_;}
|
||||
|
||||
/// Computing the water velocity without shear-thinning for the cell faces.
|
||||
/// The water velocity will be used for shear-thinning calculation.
|
||||
void computeWaterShearVelocityFaces(const V& transi, const std::vector<ADB>& kr,
|
||||
const std::vector<ADB>& phasePressure, const SolutionState& state,
|
||||
std::vector<double>& water_vel, std::vector<double>& visc_mult);
|
||||
|
||||
/// Computing the water velocity without shear-thinning for the well perforations based on the water flux rate.
|
||||
/// The water velocity will be used for shear-thinning calculation.
|
||||
void computeWaterShearVelocityWells(const SolutionState& state, WellState& xw, const ADB& cq_sw,
|
||||
std::vector<double>& water_vel_wells, std::vector<double>& visc_mult_wells);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Need to include concentration in our state variables, otherwise all is as
|
||||
/// the default blackoil model.
|
||||
struct BlackoilPolymerSolutionState : public DefaultBlackoilSolutionState
|
||||
{
|
||||
explicit BlackoilPolymerSolutionState(const int np)
|
||||
: DefaultBlackoilSolutionState(np),
|
||||
concentration( ADB::null())
|
||||
{
|
||||
}
|
||||
ADB concentration;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Providing types by template specialisation of ModelTraits for BlackoilPolymerModel.
|
||||
template <class Grid>
|
||||
struct ModelTraits< BlackoilPolymerModel<Grid> >
|
||||
{
|
||||
typedef PolymerBlackoilState ReservoirState;
|
||||
typedef WellStateFullyImplicitBlackoilPolymer WellState;
|
||||
typedef BlackoilModelParameters ModelParameters;
|
||||
typedef BlackoilPolymerSolutionState SolutionState;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#include "BlackoilPolymerModel_impl.hpp"
|
||||
|
||||
|
||||
#endif // OPM_BLACKOILPOLYMERMODEL_HEADER_INCLUDED
|
748
opm/polymer/fullyimplicit/BlackoilPolymerModel_impl.hpp
Normal file
748
opm/polymer/fullyimplicit/BlackoilPolymerModel_impl.hpp
Normal file
@ -0,0 +1,748 @@
|
||||
/*
|
||||
Copyright 2013, 2015 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2014, 2015 Dr. Blatt - HPC-Simulation-Software & Services
|
||||
Copyright 2014, 2015 Statoil ASA.
|
||||
Copyright 2015 NTNU
|
||||
Copyright 2015 IRIS AS
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_BLACKOILPOLYMERMODEL_IMPL_HEADER_INCLUDED
|
||||
#define OPM_BLACKOILPOLYMERMODEL_IMPL_HEADER_INCLUDED
|
||||
|
||||
#include <opm/polymer/fullyimplicit/BlackoilPolymerModel.hpp>
|
||||
|
||||
#include <opm/autodiff/AutoDiffBlock.hpp>
|
||||
#include <opm/autodiff/AutoDiffHelpers.hpp>
|
||||
#include <opm/autodiff/GridHelpers.hpp>
|
||||
#include <opm/autodiff/BlackoilPropsAdInterface.hpp>
|
||||
#include <opm/autodiff/GeoProps.hpp>
|
||||
#include <opm/autodiff/WellDensitySegmented.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/linalg/LinearSolverInterface.hpp>
|
||||
#include <opm/core/linalg/ParallelIstlInformation.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/common/Exceptions.hpp>
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <opm/core/well_controls.h>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class PU>
|
||||
int polymerPos(const PU& pu)
|
||||
{
|
||||
const int maxnp = Opm::BlackoilPhases::MaxNumPhases;
|
||||
int pos = 0;
|
||||
for (int phase = 0; phase < maxnp; ++phase) {
|
||||
if (pu.phase_used[phase]) {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
BlackoilPolymerModel<Grid>::BlackoilPolymerModel(const typename Base::ModelParameters& 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,
|
||||
EclipseStateConstPtr eclipse_state,
|
||||
const bool has_disgas,
|
||||
const bool has_vapoil,
|
||||
const bool has_polymer,
|
||||
const bool has_plyshlog,
|
||||
const bool has_shrate,
|
||||
const std::vector<double>& wells_rep_radius,
|
||||
const std::vector<double>& wells_perf_length,
|
||||
const std::vector<double>& wells_bore_diameter,
|
||||
const bool terminal_output)
|
||||
: Base(param, grid, fluid, geo, rock_comp_props, wells, linsolver, eclipse_state,
|
||||
has_disgas, has_vapoil, terminal_output),
|
||||
polymer_props_ad_(polymer_props_ad),
|
||||
has_polymer_(has_polymer),
|
||||
has_plyshlog_(has_plyshlog),
|
||||
has_shrate_(has_shrate),
|
||||
poly_pos_(detail::polymerPos(fluid.phaseUsage())),
|
||||
wells_rep_radius_(wells_rep_radius),
|
||||
wells_perf_length_(wells_perf_length),
|
||||
wells_bore_diameter_(wells_bore_diameter)
|
||||
{
|
||||
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());
|
||||
Base::material_name_.push_back("Polymer");
|
||||
assert(poly_pos_ == fluid_.numPhases());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
void
|
||||
BlackoilPolymerModel<Grid>::
|
||||
prepareStep(const double dt,
|
||||
ReservoirState& reservoir_state,
|
||||
WellState& well_state)
|
||||
{
|
||||
Base::prepareStep(dt, reservoir_state, well_state);
|
||||
// Initial max concentration of this time step from PolymerBlackoilState.
|
||||
cmax_ = Eigen::Map<const V>(reservoir_state.maxconcentration().data(), Opm::AutoDiffGrid::numCells(grid_));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
void
|
||||
BlackoilPolymerModel<Grid>::
|
||||
afterStep(const double /* dt */,
|
||||
ReservoirState& reservoir_state,
|
||||
WellState& /* well_state */)
|
||||
{
|
||||
computeCmax(reservoir_state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
void
|
||||
BlackoilPolymerModel<Grid>::makeConstantState(SolutionState& state) const
|
||||
{
|
||||
Base::makeConstantState(state);
|
||||
state.concentration = ADB::constant(state.concentration.value());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
std::vector<V>
|
||||
BlackoilPolymerModel<Grid>::variableStateInitials(const ReservoirState& x,
|
||||
const WellState& xw) const
|
||||
{
|
||||
std::vector<V> vars0 = Base::variableStateInitials(x, xw);
|
||||
assert(int(vars0.size()) == fluid_.numPhases() + 2);
|
||||
|
||||
// Initial polymer concentration.
|
||||
if (has_polymer_) {
|
||||
assert (not x.concentration().empty());
|
||||
const int nc = x.concentration().size();
|
||||
const V c = Eigen::Map<const V>(&x.concentration()[0], nc);
|
||||
// Concentration belongs after other reservoir vars but before well vars.
|
||||
auto concentration_pos = vars0.begin() + fluid_.numPhases();
|
||||
assert(concentration_pos == vars0.end() - 2);
|
||||
vars0.insert(concentration_pos, c);
|
||||
}
|
||||
return vars0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
std::vector<int>
|
||||
BlackoilPolymerModel<Grid>::variableStateIndices() const
|
||||
{
|
||||
std::vector<int> ind = Base::variableStateIndices();
|
||||
assert(ind.size() == 5);
|
||||
if (has_polymer_) {
|
||||
ind.resize(6);
|
||||
// Concentration belongs after other reservoir vars but before well vars.
|
||||
ind[Concentration] = fluid_.numPhases();
|
||||
// Concentration is pushing back the well vars.
|
||||
++ind[Qs];
|
||||
++ind[Bhp];
|
||||
}
|
||||
return ind;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
typename BlackoilPolymerModel<Grid>::SolutionState
|
||||
BlackoilPolymerModel<Grid>::variableStateExtractVars(const ReservoirState& x,
|
||||
const std::vector<int>& indices,
|
||||
std::vector<ADB>& vars) const
|
||||
{
|
||||
SolutionState state = Base::variableStateExtractVars(x, indices, vars);
|
||||
if (has_polymer_) {
|
||||
state.concentration = std::move(vars[indices[Concentration]]);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
void
|
||||
BlackoilPolymerModel<Grid>::computeAccum(const SolutionState& state,
|
||||
const int aix )
|
||||
{
|
||||
Base::computeAccum(state, aix);
|
||||
|
||||
// Compute accumulation of polymer equation only if needed.
|
||||
if (has_polymer_) {
|
||||
const ADB& press = state.pressure;
|
||||
const std::vector<ADB>& sat = state.saturation;
|
||||
const ADB& c = state.concentration;
|
||||
const ADB pv_mult = poroMult(press); // also computed in Base::computeAccum, could be optimized.
|
||||
const Opm::PhaseUsage& pu = fluid_.phaseUsage();
|
||||
// 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<const V>(&fluid_.porosity()[0], AutoDiffGrid::numCells(grid_));
|
||||
const double dead_pore_vol = polymer_props_ad_.deadPoreVol();
|
||||
// Compute polymer accumulation term.
|
||||
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 <class Grid>
|
||||
void BlackoilPolymerModel<Grid>::computeCmax(ReservoirState& state)
|
||||
{
|
||||
const int nc = AutoDiffGrid::numCells(grid_);
|
||||
V tmp = V::Zero(nc);
|
||||
for (int i = 0; i < nc; ++i) {
|
||||
tmp[i] = std::max(state.maxconcentration()[i], state.concentration()[i]);
|
||||
}
|
||||
std::copy(&tmp[0], &tmp[0] + nc, state.maxconcentration().begin());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
void
|
||||
BlackoilPolymerModel<Grid>::
|
||||
assembleMassBalanceEq(const SolutionState& state)
|
||||
{
|
||||
// Base::assembleMassBalanceEq(state);
|
||||
|
||||
// Compute b_p and the accumulation term b_p*s_p for each phase,
|
||||
// except gas. For gas, we compute b_g*s_g + Rs*b_o*s_o.
|
||||
// These quantities are stored in rq_[phase].accum[1].
|
||||
// The corresponding accumulation terms from the start of
|
||||
// the timestep (b^0_p*s^0_p etc.) were already computed
|
||||
// on the initial call to assemble() and stored in rq_[phase].accum[0].
|
||||
computeAccum(state, 1);
|
||||
|
||||
// Set up the common parts of the mass balance equations
|
||||
// for each active phase.
|
||||
const V transi = subset(geo_.transmissibility(), ops_.internal_faces);
|
||||
const std::vector<ADB> kr = computeRelPerm(state);
|
||||
|
||||
|
||||
if (has_plyshlog_) {
|
||||
std::vector<double> water_vel;
|
||||
std::vector<double> visc_mult;
|
||||
|
||||
computeWaterShearVelocityFaces(transi, kr, state.canonical_phase_pressures, state, water_vel, visc_mult);
|
||||
if ( !polymer_props_ad_.computeShearMultLog(water_vel, visc_mult, shear_mult_faces_) ) {
|
||||
// std::cerr << " failed in calculating the shear-multiplier " << std::endl;
|
||||
OPM_THROW(std::runtime_error, " failed in calculating the shear-multiplier. ");
|
||||
}
|
||||
}
|
||||
|
||||
for (int phaseIdx = 0; phaseIdx < fluid_.numPhases(); ++phaseIdx) {
|
||||
computeMassFlux(phaseIdx, transi, kr[canph_[phaseIdx]], state.canonical_phase_pressures[canph_[phaseIdx]], state);
|
||||
|
||||
residual_.material_balance_eq[ phaseIdx ] =
|
||||
pvdt_ * (rq_[phaseIdx].accum[1] - rq_[phaseIdx].accum[0])
|
||||
+ ops_.div*rq_[phaseIdx].mflux;
|
||||
}
|
||||
|
||||
// -------- Extra (optional) rs and rv contributions to the mass balance equations --------
|
||||
|
||||
// Add the extra (flux) terms to the mass balance equations
|
||||
// From gas dissolved in the oil phase (rs) and oil vaporized in the gas phase (rv)
|
||||
// The extra terms in the accumulation part of the equation are already handled.
|
||||
if (active_[ Oil ] && active_[ Gas ]) {
|
||||
const int po = fluid_.phaseUsage().phase_pos[ Oil ];
|
||||
const int pg = fluid_.phaseUsage().phase_pos[ Gas ];
|
||||
|
||||
const UpwindSelector<double> upwindOil(grid_, ops_,
|
||||
rq_[po].dh.value());
|
||||
const ADB rs_face = upwindOil.select(state.rs);
|
||||
|
||||
const UpwindSelector<double> upwindGas(grid_, ops_,
|
||||
rq_[pg].dh.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);
|
||||
|
||||
// OPM_AD_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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
void BlackoilPolymerModel<Grid>::addWellContributionToMassBalanceEq(const std::vector<ADB>& cq_s,
|
||||
const SolutionState& state,
|
||||
WellState& xw)
|
||||
|
||||
{
|
||||
Base::addWellContributionToMassBalanceEq(cq_s, state, xw);
|
||||
|
||||
// Add well contributions to polymer mass balance equation
|
||||
if (has_polymer_) {
|
||||
const ADB mc = computeMc(state);
|
||||
const int nc = xw.polymerInflow().size();
|
||||
const V polyin = Eigen::Map<const V>(xw.polymerInflow().data(), nc);
|
||||
const int nperf = wells().well_connpos[wells().number_of_wells];
|
||||
const std::vector<int> well_cells(wells().well_cells, wells().well_cells + nperf);
|
||||
const V poly_in_perf = subset(polyin, well_cells);
|
||||
const V poly_mc_perf = subset(mc.value(), well_cells);
|
||||
const ADB& cq_s_water = cq_s[fluid_.phaseUsage().phase_pos[Water]];
|
||||
Selector<double> injector_selector(cq_s_water.value());
|
||||
const V poly_perf = injector_selector.select(poly_in_perf, poly_mc_perf);
|
||||
const ADB cq_s_poly = cq_s_water * poly_perf;
|
||||
residual_.material_balance_eq[poly_pos_] -= superset(cq_s_poly, well_cells, nc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
void BlackoilPolymerModel<Grid>::updateState(const V& dx,
|
||||
ReservoirState& reservoir_state,
|
||||
WellState& well_state)
|
||||
{
|
||||
if (has_polymer_) {
|
||||
// Extract concentration change.
|
||||
const int np = fluid_.numPhases();
|
||||
const int nc = Opm::AutoDiffGrid::numCells(grid_);
|
||||
const V zero = V::Zero(nc);
|
||||
const int concentration_start = nc * np;
|
||||
const V dc = subset(dx, Span(nc, 1, concentration_start));
|
||||
|
||||
// Create new dx with the dc part deleted.
|
||||
V modified_dx = V::Zero(dx.size() - nc);
|
||||
modified_dx.head(concentration_start) = dx.head(concentration_start);
|
||||
const int tail_len = dx.size() - concentration_start - nc;
|
||||
modified_dx.tail(tail_len) = dx.tail(tail_len);
|
||||
|
||||
// Call base version.
|
||||
Base::updateState(modified_dx, reservoir_state, well_state);
|
||||
|
||||
// Update concentration.
|
||||
const V c_old = Eigen::Map<const V>(&reservoir_state.concentration()[0], nc, 1);
|
||||
const V c = (c_old - dc).max(zero);
|
||||
std::copy(&c[0], &c[0] + nc, reservoir_state.concentration().begin());
|
||||
} else {
|
||||
// Just forward call to base version.
|
||||
Base::updateState(dx, reservoir_state, well_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
void
|
||||
BlackoilPolymerModel<Grid>::computeMassFlux(const int actph ,
|
||||
const V& transi,
|
||||
const ADB& kr ,
|
||||
const ADB& phasePressure,
|
||||
const SolutionState& state)
|
||||
{
|
||||
Base::computeMassFlux(actph, transi, kr, phasePressure, state);
|
||||
|
||||
// Polymer treatment.
|
||||
const int canonicalPhaseIdx = canph_[ actph ];
|
||||
if (canonicalPhaseIdx == Water) {
|
||||
if (has_polymer_) {
|
||||
const std::vector<PhasePresence>& cond = phaseCondition();
|
||||
const ADB tr_mult = transMult(state.pressure);
|
||||
const ADB mu = fluidViscosity(canonicalPhaseIdx, phasePressure, state.temperature, state.rs, state.rv, cond);
|
||||
const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern());
|
||||
const ADB mc = computeMc(state);
|
||||
const ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, cmax, kr);
|
||||
const ADB inv_wat_eff_visc = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mu.value().data());
|
||||
// Reduce mobility of water phase by relperm reduction and effective viscosity increase.
|
||||
rq_[actph].mob = tr_mult * krw_eff * inv_wat_eff_visc;
|
||||
// Compute polymer mobility.
|
||||
rq_[poly_pos_].mob = tr_mult * mc * krw_eff * inv_wat_eff_visc;
|
||||
rq_[poly_pos_].b = rq_[actph].b;
|
||||
rq_[poly_pos_].dh = rq_[actph].dh;
|
||||
UpwindSelector<double> upwind(grid_, ops_, rq_[poly_pos_].dh.value());
|
||||
// Compute polymer flux.
|
||||
rq_[poly_pos_].mflux = upwind.select(rq_[poly_pos_].b * rq_[poly_pos_].mob) * (transi * rq_[poly_pos_].dh);
|
||||
// Must recompute water flux since we have to use modified mobilities.
|
||||
rq_[ actph ].mflux = upwind.select(rq_[actph].b * rq_[actph].mob) * (transi * rq_[actph].dh);
|
||||
|
||||
// applying the shear-thinning factors
|
||||
if (has_plyshlog_) {
|
||||
V shear_mult_faces_v = Eigen::Map<V>(shear_mult_faces_.data(), shear_mult_faces_.size());
|
||||
ADB shear_mult_faces_adb = ADB::constant(shear_mult_faces_v);
|
||||
rq_[poly_pos_].mflux = rq_[poly_pos_].mflux / shear_mult_faces_adb;
|
||||
rq_[actph].mflux = rq_[actph].mflux / shear_mult_faces_adb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
void
|
||||
BlackoilPolymerModel<Grid>::assemble(const ReservoirState& reservoir_state,
|
||||
WellState& well_state,
|
||||
const bool initial_assembly)
|
||||
{
|
||||
using namespace Opm::AutoDiffGrid;
|
||||
|
||||
// Possibly switch well controls and updating well state to
|
||||
// get reasonable initial conditions for the wells
|
||||
updateWellControls(well_state);
|
||||
|
||||
// Create the primary variables.
|
||||
SolutionState state = variableState(reservoir_state, well_state);
|
||||
|
||||
if (initial_assembly) {
|
||||
// Create the (constant, derivativeless) initial state.
|
||||
SolutionState state0 = state;
|
||||
makeConstantState(state0);
|
||||
// Compute initial accumulation contributions
|
||||
// and well connection pressures.
|
||||
computeAccum(state0, 0);
|
||||
computeWellConnectionPressures(state0, well_state);
|
||||
}
|
||||
|
||||
// OPM_AD_DISKVAL(state.pressure);
|
||||
// OPM_AD_DISKVAL(state.saturation[0]);
|
||||
// OPM_AD_DISKVAL(state.saturation[1]);
|
||||
// OPM_AD_DISKVAL(state.saturation[2]);
|
||||
// OPM_AD_DISKVAL(state.rs);
|
||||
// OPM_AD_DISKVAL(state.rv);
|
||||
// OPM_AD_DISKVAL(state.qs);
|
||||
// OPM_AD_DISKVAL(state.bhp);
|
||||
|
||||
// -------- Mass balance equations --------
|
||||
assembleMassBalanceEq(state);
|
||||
|
||||
// -------- Well equations ----------
|
||||
if ( ! wellsActive() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
V aliveWells;
|
||||
|
||||
const int np = wells().number_of_phases;
|
||||
std::vector<ADB> cq_s(np, ADB::null());
|
||||
|
||||
const int nw = wells().number_of_wells;
|
||||
const int nperf = wells().well_connpos[nw];
|
||||
const std::vector<int> well_cells(wells().well_cells, wells().well_cells + nperf);
|
||||
|
||||
std::vector<ADB> mob_perfcells(np, ADB::null());
|
||||
std::vector<ADB> 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);
|
||||
}
|
||||
if (param_.solve_welleq_initially_ && initial_assembly) {
|
||||
// solve the well equations as a pre-processing step
|
||||
Base::solveWellEq(mob_perfcells, b_perfcells, state, well_state);
|
||||
}
|
||||
|
||||
Base::computeWellFlux(state, mob_perfcells, b_perfcells, aliveWells, cq_s);
|
||||
|
||||
if (has_plyshlog_) {
|
||||
std::vector<double> water_vel_wells;
|
||||
std::vector<double> visc_mult_wells;
|
||||
|
||||
const int water_pos = fluid_.phaseUsage().phase_pos[Water];
|
||||
computeWaterShearVelocityWells(state, well_state, cq_s[water_pos], water_vel_wells, visc_mult_wells);
|
||||
|
||||
if ( !polymer_props_ad_.computeShearMultLog(water_vel_wells, visc_mult_wells, shear_mult_wells_) ) {
|
||||
OPM_THROW(std::runtime_error, " failed in calculating the shear factors for wells ");
|
||||
}
|
||||
|
||||
// applying the shear-thinning to the water phase
|
||||
V shear_mult_wells_v = Eigen::Map<V>(shear_mult_wells_.data(), shear_mult_wells_.size());
|
||||
ADB shear_mult_wells_adb = ADB::constant(shear_mult_wells_v);
|
||||
mob_perfcells[water_pos] = mob_perfcells[water_pos] / shear_mult_wells_adb;
|
||||
}
|
||||
|
||||
Base::computeWellFlux(state, mob_perfcells, b_perfcells, aliveWells, cq_s);
|
||||
Base::updatePerfPhaseRatesAndPressures(cq_s, state, well_state);
|
||||
Base::addWellFluxEq(cq_s, state);
|
||||
addWellContributionToMassBalanceEq(cq_s, state, well_state);
|
||||
addWellControlEq(state, well_state, aliveWells);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
ADB
|
||||
BlackoilPolymerModel<Grid>::computeMc(const SolutionState& state) const
|
||||
{
|
||||
return polymer_props_ad_.polymerWaterVelocityRatio(state.concentration);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
void
|
||||
BlackoilPolymerModel<Grid>::computeWaterShearVelocityFaces(const V& transi, const std::vector<ADB>& kr,
|
||||
const std::vector<ADB>& phasePressure, const SolutionState& state,
|
||||
std::vector<double>& water_vel, std::vector<double>& visc_mult)
|
||||
{
|
||||
|
||||
const int phase = fluid_.phaseUsage().phase_pos[Water]; // water position
|
||||
|
||||
const int canonicalPhaseIdx = canph_[phase];
|
||||
|
||||
const std::vector<PhasePresence> cond = phaseCondition();
|
||||
|
||||
const ADB tr_mult = transMult(state.pressure);
|
||||
const ADB mu = fluidViscosity(canonicalPhaseIdx, phasePressure[canonicalPhaseIdx], state.temperature, state.rs, state.rv, cond);
|
||||
rq_[phase].mob = tr_mult * kr[canonicalPhaseIdx] / mu;
|
||||
|
||||
// compute gravity potensial using the face average as in eclipse and MRST
|
||||
const ADB rho = fluidDensity(canonicalPhaseIdx, rq_[phase].b, state.rs, state.rv);
|
||||
const ADB rhoavg = ops_.caver * rho;
|
||||
rq_[ phase ].dh = ops_.ngrad * phasePressure[ canonicalPhaseIdx ] - geo_.gravity()[2] * (rhoavg * (ops_.ngrad * geo_.z().matrix()));
|
||||
if (use_threshold_pressure_) {
|
||||
applyThresholdPressures(rq_[ phase ].dh);
|
||||
}
|
||||
|
||||
const ADB& b = rq_[ phase ].b;
|
||||
const ADB& mob = rq_[ phase ].mob;
|
||||
const ADB& dh = rq_[ phase ].dh;
|
||||
UpwindSelector<double> upwind(grid_, ops_, dh.value());
|
||||
|
||||
const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern());
|
||||
const ADB mc = computeMc(state);
|
||||
ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration,
|
||||
cmax,
|
||||
kr[canonicalPhaseIdx]);
|
||||
ADB inv_wat_eff_visc = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mu.value().data());
|
||||
rq_[ phase ].mob = tr_mult * krw_eff * inv_wat_eff_visc;
|
||||
|
||||
const V& polymer_conc = state.concentration.value();
|
||||
V visc_mult_cells = polymer_props_ad_.viscMult(polymer_conc);
|
||||
V visc_mult_faces = upwind.select(visc_mult_cells);
|
||||
|
||||
size_t nface = visc_mult_faces.size();
|
||||
visc_mult.resize(nface);
|
||||
std::copy(visc_mult_faces.data(), visc_mult_faces.data() + nface, visc_mult.begin());
|
||||
|
||||
rq_[ phase ].mflux = (transi * upwind.select(b * mob)) * dh;
|
||||
|
||||
|
||||
const auto& b_faces_adb = upwind.select(b);
|
||||
std::vector<double> b_faces(b_faces_adb.value().data(), b_faces_adb.value().data() + b_faces_adb.size());
|
||||
|
||||
const auto& internal_faces = ops_.internal_faces;
|
||||
|
||||
std::vector<double> internal_face_areas;
|
||||
internal_face_areas.resize(internal_faces.size());
|
||||
|
||||
for (int i = 0; i < internal_faces.size(); ++i) {
|
||||
internal_face_areas[i] = grid_.face_areas[internal_faces[i]];
|
||||
}
|
||||
|
||||
const ADB phi = Opm::AutoDiffBlock<double>::constant(Eigen::Map<const V>(& fluid_.porosity()[0], AutoDiffGrid::numCells(grid_), 1));
|
||||
const ADB phiavg_adb = ops_.caver * phi;
|
||||
|
||||
std::vector<double> phiavg(phiavg_adb.value().data(), phiavg_adb.value().data() + phiavg_adb.size());
|
||||
|
||||
water_vel.resize(nface);
|
||||
std::copy(rq_[0].mflux.value().data(), rq_[0].mflux.value().data() + nface, water_vel.begin());
|
||||
|
||||
for (size_t i = 0; i < nface; ++i) {
|
||||
water_vel[i] = water_vel[i] / (b_faces[i] * phiavg[i] * internal_face_areas[i]);
|
||||
}
|
||||
|
||||
// for SHRATE keyword treatment
|
||||
if (has_shrate_) {
|
||||
|
||||
// get the upwind water saturation
|
||||
const Opm::PhaseUsage pu = fluid_.phaseUsage();
|
||||
const ADB& sw = state.saturation[pu.phase_pos[ Water ]];
|
||||
const ADB& sw_upwind_adb = upwind.select(sw);
|
||||
std::vector<double> sw_upwind(sw_upwind_adb.value().data(), sw_upwind_adb.value().data() + sw_upwind_adb.size());
|
||||
|
||||
// get the absolute permeability for the faces
|
||||
std::vector<double> perm;
|
||||
perm.resize(transi.size());
|
||||
|
||||
for (int i = 0; i < transi.size(); ++i) {
|
||||
perm[i] = transi[i] / internal_faces[i];
|
||||
}
|
||||
|
||||
// get the upwind krw_eff
|
||||
const ADB& krw_adb = upwind.select(krw_eff);
|
||||
std::vector<double> krw_upwind(krw_adb.value().data(), krw_adb.value().data() + krw_adb.size());
|
||||
|
||||
const double& shrate_const = polymer_props_ad_.shrate();
|
||||
|
||||
const double epsilon = std::numeric_limits<double>::epsilon();
|
||||
// std::cout << "espilon is " << epsilon << std::endl;
|
||||
// std::cin.ignore();
|
||||
|
||||
for (size_t i = 0; i < water_vel.size(); ++i) {
|
||||
// assuming only when upwinding water saturation is not zero
|
||||
// there will be non-zero water velocity
|
||||
if (std::abs(water_vel[i]) < epsilon) {
|
||||
continue;
|
||||
}
|
||||
|
||||
water_vel[i] *= shrate_const * std::sqrt(phiavg[i] / (perm[i] * sw_upwind[i] * krw_upwind[i]));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
void
|
||||
BlackoilPolymerModel<Grid>::computeWaterShearVelocityWells(const SolutionState& state, WellState& xw, const ADB& cq_sw,
|
||||
std::vector<double>& water_vel_wells, std::vector<double>& visc_mult_wells)
|
||||
{
|
||||
if( ! wellsActive() ) return ;
|
||||
|
||||
const int nw = wells().number_of_wells;
|
||||
const int nperf = wells().well_connpos[nw];
|
||||
const std::vector<int> well_cells(wells().well_cells, wells().well_cells + nperf);
|
||||
|
||||
water_vel_wells.resize(cq_sw.size());
|
||||
std::copy(cq_sw.value().data(), cq_sw.value().data() + cq_sw.size(), water_vel_wells.begin());
|
||||
|
||||
const V& polymer_conc = state.concentration.value();
|
||||
|
||||
V visc_mult_cells = polymer_props_ad_.viscMult(polymer_conc);
|
||||
V visc_mult_wells_v = subset(visc_mult_cells, well_cells);
|
||||
|
||||
visc_mult_wells.resize(visc_mult_wells_v.size());
|
||||
std::copy(visc_mult_wells_v.data(), visc_mult_wells_v.data() + visc_mult_wells_v.size(), visc_mult_wells.begin());
|
||||
|
||||
const int water_pos = fluid_.phaseUsage().phase_pos[Water];
|
||||
ADB b_perfcells = subset(rq_[water_pos].b, well_cells);
|
||||
|
||||
const ADB& p_perfcells = subset(state.pressure, well_cells);
|
||||
const V& cdp = well_perforation_pressure_diffs_;
|
||||
const ADB perfpressure = (wops_.w2p * state.bhp) + cdp;
|
||||
// Pressure drawdown (also used to determine direction of flow)
|
||||
const ADB drawdown = p_perfcells - perfpressure;
|
||||
|
||||
// selects injection perforations
|
||||
V selectInjectingPerforations = V::Zero(nperf);
|
||||
for (int c = 0; c < nperf; ++c) {
|
||||
if (drawdown.value()[c] < 0) {
|
||||
selectInjectingPerforations[c] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// for the injection wells
|
||||
for (size_t i = 0; i < well_cells.size(); ++i) {
|
||||
if (xw.polymerInflow()[well_cells[i]] == 0. && selectInjectingPerforations[i] == 1) { // maybe comparison with epsilon threshold
|
||||
visc_mult_wells[i] = 1.;
|
||||
}
|
||||
}
|
||||
|
||||
const ADB phi = Opm::AutoDiffBlock<double>::constant(Eigen::Map<const V>(& fluid_.porosity()[0], AutoDiffGrid::numCells(grid_), 1));
|
||||
const ADB phi_wells_adb = subset(phi, well_cells);
|
||||
|
||||
std::vector<double> phi_wells(phi_wells_adb.value().data(), phi_wells_adb.value().data() + phi_wells_adb.size());
|
||||
|
||||
std::vector<double> b_wells(b_perfcells.value().data(), b_perfcells.value().data() + b_perfcells.size());
|
||||
|
||||
for (size_t i = 0; i < water_vel_wells.size(); ++i) {
|
||||
water_vel_wells[i] = b_wells[i] * water_vel_wells[i] / (phi_wells[i] * 2. * M_PI * wells_rep_radius_[i] * wells_perf_length_[i]);
|
||||
// TODO: CHECK to make sure this formulation is corectly used. Why muliplied by bW.
|
||||
// Although this formulation works perfectly with the tests compared with other formulations
|
||||
}
|
||||
|
||||
// for SHRATE treatment
|
||||
if (has_shrate_) {
|
||||
const double& shrate_const = polymer_props_ad_.shrate();
|
||||
for (size_t i = 0; i < water_vel_wells.size(); ++i) {
|
||||
water_vel_wells[i] = shrate_const * water_vel_wells[i] / wells_bore_diameter_[i];
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_BLACKOILPOLYMERMODEL_IMPL_HEADER_INCLUDED
|
1106
opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp
Normal file
1106
opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,263 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_FULLYIMPLICITCOMPRESSIBLEPOLYMERSOLVER_HEADER_INCLUDED
|
||||
#define OPM_FULLYIMPLICITCOMPRESSIBLEPOLYMERSOLVER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/autodiff/AutoDiffBlock.hpp>
|
||||
#include <opm/autodiff/AutoDiffHelpers.hpp>
|
||||
#include <opm/autodiff/BlackoilPropsAdInterface.hpp>
|
||||
#include <opm/autodiff/NewtonIterationBlackoilInterface.hpp>
|
||||
#include <opm/autodiff/LinearisedBlackoilResidual.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
#include <opm/polymer/fullyimplicit/WellStateFullyImplicitBlackoilPolymer.hpp>
|
||||
#include <opm/polymer/fullyimplicit/PolymerPropsAd.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
struct Wells;
|
||||
|
||||
namespace Opm {
|
||||
|
||||
class DerivedGeology;
|
||||
class RockCompressibility;
|
||||
class NewtonIterationBlackoilInterface;
|
||||
class PolymerBlackoilState;
|
||||
class WellStateFullyImplicitBlackoil;
|
||||
|
||||
/// A fully implicit solver for the oil-water with polymer problem.
|
||||
///
|
||||
/// The simulator is capable of handling oil-water-polymer problems
|
||||
/// 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.
|
||||
class FullyImplicitCompressiblePolymerSolver
|
||||
{
|
||||
public:
|
||||
/// 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] 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] polymer_props_ad polymer properties
|
||||
/// \param[in] wells well structure
|
||||
/// \param[in] linsolver linear solver
|
||||
FullyImplicitCompressiblePolymerSolver(const UnstructuredGrid& grid ,
|
||||
const BlackoilPropsAdInterface& fluid,
|
||||
const DerivedGeology& geo ,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
const PolymerPropsAd& polymer_props_ad,
|
||||
const Wells& wells,
|
||||
const NewtonIterationBlackoilInterface& linsolver);
|
||||
|
||||
/// Take a single forward step, modifiying
|
||||
/// state.pressure()
|
||||
/// state.faceflux()
|
||||
/// state.saturation()
|
||||
/// state.concentration()
|
||||
/// wstate.bhp()
|
||||
/// \param[in] dt time step size
|
||||
/// \param[in] state reservoir state
|
||||
/// \param[in] wstate well state
|
||||
/// \param[in] polymer_inflow polymer influx
|
||||
int
|
||||
step(const double dt,
|
||||
PolymerBlackoilState& state ,
|
||||
WellStateFullyImplicitBlackoilPolymer& wstate);
|
||||
|
||||
int nonlinearIterations() const;
|
||||
int linearIterations() const;
|
||||
|
||||
/// Not used by this class except to satisfy interface requirements.
|
||||
typedef parameter::ParameterGroup SolverParameters;
|
||||
|
||||
/// There is no separate model class for this solver, return itself.
|
||||
const FullyImplicitCompressiblePolymerSolver& model() const;
|
||||
|
||||
/// Evaluate the relative changes in the physical variables.
|
||||
double relativeChange(const PolymerBlackoilState& previous,
|
||||
const PolymerBlackoilState& current ) const;
|
||||
|
||||
private:
|
||||
typedef AutoDiffBlock<double> ADB;
|
||||
typedef ADB::V V;
|
||||
typedef ADB::M M;
|
||||
typedef Eigen::Array<double,
|
||||
Eigen::Dynamic,
|
||||
Eigen::Dynamic,
|
||||
Eigen::RowMajor> DataBlock;
|
||||
|
||||
struct ReservoirResidualQuant {
|
||||
ReservoirResidualQuant();
|
||||
std::vector<ADB> 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)
|
||||
std::vector<ADB> ads; // Adsorption term.
|
||||
};
|
||||
|
||||
struct SolutionState {
|
||||
SolutionState(const int np);
|
||||
ADB pressure;
|
||||
ADB temperature;
|
||||
std::vector<ADB> saturation;
|
||||
ADB concentration;
|
||||
ADB qs;
|
||||
ADB bhp;
|
||||
};
|
||||
|
||||
struct WellOps {
|
||||
WellOps(const Wells& wells);
|
||||
Eigen::SparseMatrix<double> w2p; // well -> perf (scatter)
|
||||
Eigen::SparseMatrix<double> p2w; // perf -> well (gather)
|
||||
};
|
||||
|
||||
enum { Water = BlackoilPropsAdInterface::Water,
|
||||
Oil = BlackoilPropsAdInterface::Oil };
|
||||
|
||||
// Member data
|
||||
const UnstructuredGrid& grid_;
|
||||
const BlackoilPropsAdInterface& fluid_;
|
||||
const DerivedGeology& geo_;
|
||||
const RockCompressibility* rock_comp_props_;
|
||||
const PolymerPropsAd& polymer_props_ad_;
|
||||
const Wells& wells_;
|
||||
const NewtonIterationBlackoilInterface& linsolver_;
|
||||
const std::vector<int> cells_; // All grid cells
|
||||
HelperOps ops_;
|
||||
const WellOps wops_;
|
||||
const M grav_;
|
||||
V cmax_;
|
||||
std::vector<PhasePresence> phaseCondition_;
|
||||
std::vector<ReservoirResidualQuant> rq_;
|
||||
// The mass_balance vector has one element for each active phase,
|
||||
// each of which has size equal to the number of cells.
|
||||
// The well_eq has size equal to the number of wells.
|
||||
LinearisedBlackoilResidual residual_;
|
||||
|
||||
unsigned int newtonIterations_;
|
||||
unsigned int linearIterations_;
|
||||
|
||||
// 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
|
||||
assemble(const double dt,
|
||||
const PolymerBlackoilState& x,
|
||||
const WellStateFullyImplicitBlackoil& xw,
|
||||
const std::vector<double>& polymer_inflow);
|
||||
|
||||
V solveJacobianSystem() const;
|
||||
|
||||
void updateState(const V& dx,
|
||||
PolymerBlackoilState& state,
|
||||
WellStateFullyImplicitBlackoil& well_state) const;
|
||||
|
||||
std::vector<ADB>
|
||||
computeRelPerm(const SolutionState& state) const;
|
||||
|
||||
std::vector<ADB>
|
||||
computePressures(const SolutionState& state) const;
|
||||
|
||||
void
|
||||
computeMassFlux(const int actph ,
|
||||
const V& transi,
|
||||
const std::vector<ADB>& kr ,
|
||||
const SolutionState& state );
|
||||
|
||||
void
|
||||
computeMassFlux(const V& trans,
|
||||
const ADB& mc,
|
||||
const ADB& kro,
|
||||
const ADB& krw_eff,
|
||||
const SolutionState& state);
|
||||
|
||||
std::vector<ADB>
|
||||
computeFracFlow(const ADB& kro,
|
||||
const ADB& krw_eff,
|
||||
const ADB& c) const;
|
||||
|
||||
void
|
||||
computeCmax(PolymerBlackoilState& state);
|
||||
|
||||
ADB
|
||||
computeMc(const SolutionState& state) const;
|
||||
|
||||
ADB
|
||||
rockPorosity(const ADB& p) const;
|
||||
|
||||
ADB
|
||||
rockPermeability(const ADB& p) const;
|
||||
|
||||
double
|
||||
residualNorm() const;
|
||||
|
||||
ADB
|
||||
fluidViscosity(const int phase,
|
||||
const ADB& p ,
|
||||
const ADB& T ,
|
||||
const std::vector<PhasePresence>& cond,
|
||||
const std::vector<int>& cells) const;
|
||||
|
||||
ADB
|
||||
fluidReciprocFVF(const int phase,
|
||||
const ADB& p ,
|
||||
const ADB& T ,
|
||||
const std::vector<PhasePresence>& cond,
|
||||
const std::vector<int>& cells) const;
|
||||
|
||||
ADB
|
||||
fluidDensity(const int phase,
|
||||
const ADB& p ,
|
||||
const ADB& T ,
|
||||
const std::vector<PhasePresence>& cond,
|
||||
const std::vector<int>& cells) const;
|
||||
|
||||
ADB
|
||||
poroMult(const ADB& p) const;
|
||||
|
||||
ADB
|
||||
transMult(const ADB& p) const;
|
||||
|
||||
const std::vector<PhasePresence>
|
||||
phaseCondition() const { return phaseCondition_; }
|
||||
|
||||
void
|
||||
classifyCondition(const PolymerBlackoilState& state);
|
||||
};
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
#endif // OPM_FULLYIMPLICITCOMPRESSIBLEPOLYMERSOLVER_HEADER_INCLUDED
|
338
opm/polymer/fullyimplicit/PolymerPropsAd.cpp
Normal file
338
opm/polymer/fullyimplicit/PolymerPropsAd.cpp
Normal file
@ -0,0 +1,338 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <opm/autodiff/AutoDiffBlock.hpp>
|
||||
#include <opm/autodiff/AutoDiffHelpers.hpp>
|
||||
#include <opm/polymer/fullyimplicit/PolymerPropsAd.hpp>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
typedef PolymerPropsAd::ADB ADB;
|
||||
typedef PolymerPropsAd::V V;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
double
|
||||
PolymerPropsAd::rockDensity() const
|
||||
{
|
||||
return polymer_props_.rockDensity();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
double
|
||||
PolymerPropsAd::deadPoreVol() const
|
||||
{
|
||||
return polymer_props_.deadPoreVol();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
double
|
||||
PolymerPropsAd::cMax() const
|
||||
{
|
||||
return polymer_props_.cMax();
|
||||
}
|
||||
|
||||
const std::vector<double>&
|
||||
PolymerPropsAd::shearWaterVelocity() const
|
||||
{
|
||||
return polymer_props_.shearWaterVelocity();
|
||||
}
|
||||
|
||||
const std::vector<double>&
|
||||
PolymerPropsAd::shearViscosityReductionFactor() const
|
||||
{
|
||||
return polymer_props_.shearViscosityReductionFactor();
|
||||
}
|
||||
|
||||
double
|
||||
PolymerPropsAd::plyshlogRefConc() const
|
||||
{
|
||||
return polymer_props_.plyshlogRefConc();
|
||||
}
|
||||
|
||||
bool
|
||||
PolymerPropsAd::hasPlyshlogRefSalinity() const
|
||||
{
|
||||
return polymer_props_.hasPlyshlogRefSalinity();
|
||||
}
|
||||
|
||||
bool
|
||||
PolymerPropsAd::hasPlyshlogRefTemp() const
|
||||
{
|
||||
return polymer_props_.hasPlyshlogRefTemp();
|
||||
}
|
||||
|
||||
double
|
||||
PolymerPropsAd::plyshlogRefSalinity() const
|
||||
{
|
||||
return polymer_props_.plyshlogRefSalinity();
|
||||
}
|
||||
|
||||
double
|
||||
PolymerPropsAd::plyshlogRefTemp() const
|
||||
{
|
||||
return polymer_props_.plyshlogRefTemp();
|
||||
}
|
||||
|
||||
double PolymerPropsAd::shrate() const
|
||||
{
|
||||
if (polymer_props_.hasShrate()) {
|
||||
return polymer_props_.shrate();
|
||||
} else {
|
||||
OPM_THROW(std::logic_error, "the SHRATE keyword is not specified while requested \n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
PolymerPropsAd::viscMult(double c) const
|
||||
{
|
||||
return polymer_props_.viscMult(c);
|
||||
}
|
||||
|
||||
V
|
||||
PolymerPropsAd::viscMult(const V& c) const
|
||||
{
|
||||
int nc = c.size();
|
||||
V visc_mult(nc);
|
||||
for (int i = 0; i < nc; ++i) {
|
||||
visc_mult[i] = polymer_props_.viscMult(c[i]);
|
||||
}
|
||||
return visc_mult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PolymerPropsAd::PolymerPropsAd(const PolymerProperties& polymer_props)
|
||||
: polymer_props_ (polymer_props)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
PolymerPropsAd::~PolymerPropsAd()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
V PolymerPropsAd::effectiveInvWaterVisc(const V& c,
|
||||
const double* visc) const
|
||||
{
|
||||
const int nc = c.size();
|
||||
V inv_mu_w_eff(nc);
|
||||
for (int i = 0; i < nc; ++i) {
|
||||
double im = 0;
|
||||
polymer_props_.effectiveInvVisc(c(i), visc, im);
|
||||
inv_mu_w_eff(i) = im;
|
||||
}
|
||||
|
||||
return inv_mu_w_eff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ADB PolymerPropsAd::effectiveInvWaterVisc(const ADB& c,
|
||||
const double* visc) const
|
||||
{
|
||||
const int nc = c.size();
|
||||
V inv_mu_w_eff(nc);
|
||||
V dinv_mu_w_eff(nc);
|
||||
for (int i = 0; i < nc; ++i) {
|
||||
double im = 0, dim = 0;
|
||||
polymer_props_.effectiveInvViscWithDer(c.value()(i), visc, im, dim);
|
||||
inv_mu_w_eff(i) = im;
|
||||
dinv_mu_w_eff(i) = dim;
|
||||
}
|
||||
ADB::M dim_diag(dinv_mu_w_eff.matrix().asDiagonal());
|
||||
const int num_blocks = c.numBlocks();
|
||||
std::vector<ADB::M> jacs(num_blocks);
|
||||
for (int block = 0; block < num_blocks; ++block) {
|
||||
jacs[block] = dim_diag * c.derivative()[block];
|
||||
}
|
||||
return ADB::function(std::move(inv_mu_w_eff), std::move(jacs));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
V PolymerPropsAd::polymerWaterVelocityRatio(const V& c) const
|
||||
{
|
||||
const int nc = c.size();
|
||||
V mc(nc);
|
||||
|
||||
for (int i = 0; i < nc; ++i) {
|
||||
double m = 0;
|
||||
polymer_props_.computeMc(c(i), m);
|
||||
mc(i) = m;
|
||||
}
|
||||
|
||||
return mc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ADB PolymerPropsAd::polymerWaterVelocityRatio(const ADB& c) const
|
||||
{
|
||||
|
||||
const int nc = c.size();
|
||||
V mc(nc);
|
||||
V dmc(nc);
|
||||
|
||||
for (int i = 0; i < nc; ++i) {
|
||||
double m = 0;
|
||||
double dm = 0;
|
||||
polymer_props_.computeMcWithDer(c.value()(i), m, dm);
|
||||
|
||||
mc(i) = m;
|
||||
dmc(i) = dm;
|
||||
}
|
||||
|
||||
ADB::M dmc_diag(dmc.matrix().asDiagonal());
|
||||
const int num_blocks = c.numBlocks();
|
||||
std::vector<ADB::M> jacs(num_blocks);
|
||||
for (int block = 0; block < num_blocks; ++block) {
|
||||
jacs[block] = dmc_diag * c.derivative()[block];
|
||||
}
|
||||
|
||||
return ADB::function(std::move(mc), std::move(jacs));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
V PolymerPropsAd::adsorption(const V& c, const V& cmax_cells) const
|
||||
{
|
||||
const int nc = c.size();
|
||||
V ads(nc);
|
||||
|
||||
for (int i = 0; i < nc; ++i) {
|
||||
double c_ads = 0;
|
||||
polymer_props_.adsorption(c(i), cmax_cells(i), c_ads);
|
||||
|
||||
ads(i) = c_ads;
|
||||
}
|
||||
|
||||
return ads;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ADB PolymerPropsAd::adsorption(const ADB& c, const ADB& cmax_cells) const
|
||||
{
|
||||
const int nc = c.value().size();
|
||||
|
||||
V ads(nc);
|
||||
V dads(nc);
|
||||
|
||||
for (int i = 0; i < nc; ++i) {
|
||||
double c_ads = 0;
|
||||
double dc_ads = 0;
|
||||
polymer_props_.adsorptionWithDer(c.value()(i), cmax_cells.value()(i), c_ads, dc_ads);
|
||||
ads(i) = c_ads;
|
||||
dads(i) = dc_ads;
|
||||
}
|
||||
|
||||
ADB::M dads_diag(dads.matrix().asDiagonal());
|
||||
int num_blocks = c.numBlocks();
|
||||
std::vector<ADB::M> jacs(num_blocks);
|
||||
for (int block = 0; block < num_blocks; ++block) {
|
||||
jacs[block] = dads_diag * c.derivative()[block];
|
||||
}
|
||||
|
||||
return ADB::function(std::move(ads), std::move(jacs));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
V
|
||||
PolymerPropsAd::effectiveRelPerm(const V& c,
|
||||
const V& cmax_cells,
|
||||
const V& krw) const
|
||||
{
|
||||
const int nc = c.size();
|
||||
|
||||
V one = V::Ones(nc);
|
||||
V ads = adsorption(c, cmax_cells);
|
||||
double max_ads = polymer_props_.cMaxAds();
|
||||
double res_factor = polymer_props_.resFactor();
|
||||
double factor = (res_factor -1.) / max_ads;
|
||||
V rk = one + factor * ads;
|
||||
|
||||
return krw / rk;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ADB
|
||||
PolymerPropsAd::effectiveRelPerm(const ADB& c,
|
||||
const ADB& cmax_cells,
|
||||
const ADB& krw) const
|
||||
{
|
||||
const int nc = c.value().size();
|
||||
V one = V::Ones(nc);
|
||||
ADB ads = adsorption(c, cmax_cells);
|
||||
V krw_eff = effectiveRelPerm(c.value(), cmax_cells.value(), krw.value());
|
||||
|
||||
double max_ads = polymer_props_.cMaxAds();
|
||||
double res_factor = polymer_props_.resFactor();
|
||||
double factor = (res_factor - 1.) / max_ads;
|
||||
ADB rk = one + ads * factor;
|
||||
|
||||
return krw / rk;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PolymerPropsAd::computeShearMultLog(std::vector<double>& water_vel, std::vector<double>& visc_mult, std::vector<double>& shear_mult) const
|
||||
{
|
||||
return polymer_props_.computeShearMultLog(water_vel, visc_mult, shear_mult);
|
||||
}
|
||||
|
||||
|
||||
}// namespace Opm
|
150
opm/polymer/fullyimplicit/PolymerPropsAd.hpp
Normal file
150
opm/polymer/fullyimplicit/PolymerPropsAd.hpp
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_POLYMERPROPSAD_HEADED_INLCUDED
|
||||
#define OPM_POLYMERPROPSAD_HEADED_INLCUDED
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <opm/autodiff/AutoDiffBlock.hpp>
|
||||
#include <opm/autodiff/AutoDiffHelpers.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
class PolymerPropsAd
|
||||
{
|
||||
public:
|
||||
/// \return Reference rock density.
|
||||
double rockDensity() const;
|
||||
|
||||
/// \return The value of dead pore volume.
|
||||
double deadPoreVol() const;
|
||||
|
||||
/// \return The max concentration injected.
|
||||
double cMax() const;
|
||||
|
||||
/// \ return The water velcoity or shear rate in the PLYSHLOG table
|
||||
const std::vector<double>& shearWaterVelocity() const;
|
||||
|
||||
/// \ return The viscosity reducation factor in the PLYSHLOG table
|
||||
const std::vector<double>& shearViscosityReductionFactor() const;
|
||||
|
||||
/// \ return The reference polymer concentration for PLYSHLOG table
|
||||
double plyshlogRefConc() const;
|
||||
|
||||
/// \ return The flag indicating if reference salinity is specified in PLYSHLOG keyword
|
||||
bool hasPlyshlogRefSalinity() const;
|
||||
|
||||
/// \ return The flag indicating if reference temperature is specified in PLYSHLOG keyword
|
||||
bool hasPlyshlogRefTemp() const;
|
||||
|
||||
/// \ return The reference salinity in PLYSHLOG keyword
|
||||
double plyshlogRefSalinity() const;
|
||||
|
||||
/// \ return The reference temperature in PLYSHLOG keyword
|
||||
double plyshlogRefTemp() const;
|
||||
|
||||
/// \ return the value of SHRATE
|
||||
double shrate() const;
|
||||
|
||||
double viscMult(double c) const; // multipler interpolated from PLYVISC table
|
||||
|
||||
typedef AutoDiffBlock<double> ADB;
|
||||
typedef ADB::V V;
|
||||
|
||||
V viscMult(const V& c) const;
|
||||
/// \param[in] c Array of n polymer concentraion values.
|
||||
/// \return Array of n viscosity multiplier from PLVISC table.
|
||||
|
||||
/// Constructor wrapping a polymer props.
|
||||
PolymerPropsAd(const PolymerProperties& polymer_props);
|
||||
|
||||
/// Destructor.
|
||||
~PolymerPropsAd();
|
||||
|
||||
/// \param[in] c Array of n polymer concentraion values.
|
||||
/// \param[in] visc Array of 2 viscosity value.
|
||||
/// \return value of inverse effective water viscosity.
|
||||
V
|
||||
effectiveInvWaterVisc(const V& c,const double* visc) const;
|
||||
|
||||
/// \param[in] c Array of n polymer concentraion values.
|
||||
/// \param[in] visc Array of 2 viscosity value
|
||||
/// \return value of inverse effective water viscosity.
|
||||
ADB
|
||||
effectiveInvWaterVisc(const ADB& c,const double* visc) const;
|
||||
|
||||
/// \param[in] c Array of n polymer concentraion values.
|
||||
/// \return Array of n mc values, here mc means m(c) * c.
|
||||
V
|
||||
polymerWaterVelocityRatio(const V& c) const;
|
||||
|
||||
/// \param[in] c Array of n polymer concentraion values.
|
||||
/// \return Array of n mc values, here mc means m(c) * c.
|
||||
ADB
|
||||
polymerWaterVelocityRatio(const ADB& c) const;
|
||||
|
||||
/// \param[in] c Array of n polymer concentraion values.
|
||||
/// \param[in] cmax_cells Array of n polymer concentraion values
|
||||
/// that the cell experienced.
|
||||
/// \return Array of n adsorption values.
|
||||
V
|
||||
adsorption(const V& c, const V& cmax_cells) const;
|
||||
|
||||
/// \param[in] c Array of n polymer concentraion values.
|
||||
/// \param[in] cmax_cells Array of n polymer concentraion values
|
||||
/// that the cell experienced.
|
||||
/// \return Array of n adsorption values.
|
||||
ADB
|
||||
adsorption(const ADB& c, const ADB& cmax_cells) const;
|
||||
|
||||
/// \param[in] c Array of n polymer concentraion values.
|
||||
/// \param[in] cmax_cells Array of n polymer concentraion values
|
||||
/// that the cell experienced.
|
||||
/// \param[in] relperm Array of n relative water relperm values.
|
||||
/// \return Array of n adsorption values.
|
||||
V
|
||||
effectiveRelPerm(const V& c, const V& cmax_cells, const V& relperm) const;
|
||||
|
||||
|
||||
/// \param[in] c Array of n polymer concentraion values.
|
||||
/// \param[in] cmax_cells Array of n polymer concentraion values
|
||||
/// that the cell experienced.
|
||||
/// \param[in] relperm Array of n relative water relperm values.
|
||||
/// \return Array of n adsorption values.
|
||||
ADB
|
||||
effectiveRelPerm(const ADB& c, const ADB& cmax_cells, const ADB& krw) const;
|
||||
|
||||
/// \param[in] water_vel Array of the n values of water velocity or shear rate.
|
||||
/// \param[in] visc_mult Array of the n values of the viscosity multiplier from PLYVISC table.
|
||||
/// \parma[out] shear_mult Array of the n values of calculated shear multiplier with PLYSHLOG keyword.
|
||||
/// \return TRUE if the calculation of shear multiplier is sucessful,
|
||||
/// FALSE if the calculation of shear multplier is failed.
|
||||
bool computeShearMultLog(std::vector<double>& water_vel, std::vector<double>& visc_mult, std::vector<double>& shear_mult) const;
|
||||
|
||||
|
||||
private:
|
||||
const PolymerProperties& polymer_props_;
|
||||
};
|
||||
|
||||
} //namespace Opm
|
||||
|
||||
#endif// OPM_POLYMERPROPSAD_HEADED_INLCUDED
|
@ -0,0 +1,170 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_SIMULATORFULLYIMPLICITBLACKOILPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_SIMULATORFULLYIMPLICITBLACKOILPOLYMER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/autodiff/SimulatorBase.hpp>
|
||||
#include <opm/autodiff/SimulatorFullyImplicitBlackoilOutput.hpp>
|
||||
#include <opm/polymer/fullyimplicit/BlackoilPolymerModel.hpp>
|
||||
#include <opm/polymer/fullyimplicit/WellStateFullyImplicitBlackoilPolymer.hpp>
|
||||
#include <opm/polymer/PolymerBlackoilState.hpp>
|
||||
#include <opm/polymer/PolymerInflow.hpp>
|
||||
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
|
||||
#include <opm/autodiff/GeoProps.hpp>
|
||||
#include <opm/autodiff/BlackoilPropsAdInterface.hpp>
|
||||
#include <opm/autodiff/RateConverter.hpp>
|
||||
#include <opm/autodiff/NonlinearSolver.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
#include <opm/core/pressure/flow_bc.h>
|
||||
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
//#include <opm/core/simulator/AdaptiveSimulatorTimer.hpp>
|
||||
#include <opm/core/utility/StopWatch.hpp>
|
||||
#include <opm/core/io/vtk/writeVtkData.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/miscUtilitiesBlackoil.hpp>
|
||||
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
//#include <opm/core/simulator/AdaptiveTimeStepping.hpp>
|
||||
#include <opm/core/transport/reorder/TransportSolverCompressibleTwophaseReorder.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/WellProductionProperties.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
template <class GridT>
|
||||
class SimulatorFullyImplicitBlackoilPolymer;
|
||||
|
||||
template<class GridT>
|
||||
struct SimulatorTraits<SimulatorFullyImplicitBlackoilPolymer<GridT> >
|
||||
{
|
||||
typedef WellStateFullyImplicitBlackoilPolymer WellState;
|
||||
typedef PolymerBlackoilState ReservoirState;
|
||||
typedef BlackoilOutputWriter OutputWriter;
|
||||
typedef GridT Grid;
|
||||
typedef BlackoilPolymerModel<Grid> Model;
|
||||
typedef NonlinearSolver<Model> Solver;
|
||||
};
|
||||
|
||||
/// Class collecting all necessary components for a blackoil simulation with polymer
|
||||
/// injection.
|
||||
template <class GridT>
|
||||
class SimulatorFullyImplicitBlackoilPolymer
|
||||
: public SimulatorBase<SimulatorFullyImplicitBlackoilPolymer<GridT> >
|
||||
{
|
||||
typedef SimulatorFullyImplicitBlackoilPolymer<GridT> ThisType;
|
||||
typedef SimulatorBase<ThisType> BaseType;
|
||||
|
||||
typedef SimulatorTraits<ThisType> Traits;
|
||||
typedef typename Traits::Solver Solver;
|
||||
|
||||
public:
|
||||
SimulatorFullyImplicitBlackoilPolymer(const parameter::ParameterGroup& param,
|
||||
const GridT& grid,
|
||||
DerivedGeology& geo,
|
||||
BlackoilPropsAdInterface& props,
|
||||
const PolymerPropsAd& polymer_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
NewtonIterationBlackoilInterface& linsolver,
|
||||
const double* gravity,
|
||||
const bool disgas,
|
||||
const bool vapoil,
|
||||
const bool polymer,
|
||||
const bool plyshlog,
|
||||
const bool shrate,
|
||||
std::shared_ptr<EclipseState> eclipse_state,
|
||||
BlackoilOutputWriter& output_writer,
|
||||
Opm::DeckConstPtr& deck,
|
||||
const std::vector<double>& threshold_pressures_by_face);
|
||||
|
||||
std::unique_ptr<Solver> createSolver(const Wells* wells);
|
||||
|
||||
|
||||
void handleAdditionalWellInflow(SimulatorTimer& timer,
|
||||
WellsManager& wells_manager,
|
||||
typename BaseType::WellState& well_state,
|
||||
const Wells* wells);
|
||||
|
||||
|
||||
private:
|
||||
const PolymerPropsAd& polymer_props_;
|
||||
bool has_polymer_;
|
||||
// flag for PLYSHLOG keyword
|
||||
bool has_plyshlog_;
|
||||
// flag for SHRATE keyword
|
||||
bool has_shrate_;
|
||||
DeckConstPtr deck_;
|
||||
|
||||
std::vector<double> wells_rep_radius_;
|
||||
std::vector<double> wells_perf_length_;
|
||||
std::vector<double> wells_bore_diameter_;
|
||||
|
||||
// generate the mapping from Cartesian grid cells to global compressed cells,
|
||||
// copied from opm-core, to be used in function computeRepRadiusPerfLength()
|
||||
static void
|
||||
setupCompressedToCartesian(const int* global_cell, int number_of_cells, std::map<int,int>& cartesian_to_compressed);
|
||||
|
||||
// calculate the representative radius and length for for well peforations
|
||||
// and store the wellbore diameters
|
||||
// it will be used in the shear-thinning calcluation only.
|
||||
void
|
||||
computeRepRadiusPerfLength(const Opm::EclipseStateConstPtr eclipseState,
|
||||
const size_t timeStep,
|
||||
const GridT& grid,
|
||||
std::vector<double>& wells_rep_radius,
|
||||
std::vector<double>& wells_perf_length,
|
||||
std::vector<double>& wells_bore_diameter);
|
||||
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#include "SimulatorFullyImplicitBlackoilPolymer_impl.hpp"
|
||||
|
||||
#endif // OPM_SIMULATORFULLYIMPLICITBLACKOILPOLYMER_HEADER_INCLUDED
|
@ -0,0 +1,265 @@
|
||||
/*
|
||||
Copyright 2013 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2014 IRIS AS
|
||||
Copyright 2014 STATOIL ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
template <class GridT>
|
||||
SimulatorFullyImplicitBlackoilPolymer<GridT>::
|
||||
SimulatorFullyImplicitBlackoilPolymer(const parameter::ParameterGroup& param,
|
||||
const GridT& grid,
|
||||
DerivedGeology& geo,
|
||||
BlackoilPropsAdInterface& props,
|
||||
const PolymerPropsAd& polymer_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
NewtonIterationBlackoilInterface& linsolver,
|
||||
const double* gravity,
|
||||
const bool has_disgas,
|
||||
const bool has_vapoil,
|
||||
const bool has_polymer,
|
||||
const bool has_plyshlog,
|
||||
const bool has_shrate,
|
||||
std::shared_ptr<EclipseState> eclipse_state,
|
||||
BlackoilOutputWriter& output_writer,
|
||||
Opm::DeckConstPtr& deck,
|
||||
const std::vector<double>& threshold_pressures_by_face)
|
||||
: BaseType(param,
|
||||
grid,
|
||||
geo,
|
||||
props,
|
||||
rock_comp_props,
|
||||
linsolver,
|
||||
gravity,
|
||||
has_disgas,
|
||||
has_vapoil,
|
||||
eclipse_state,
|
||||
output_writer,
|
||||
threshold_pressures_by_face)
|
||||
, polymer_props_(polymer_props)
|
||||
, has_polymer_(has_polymer)
|
||||
, has_plyshlog_(has_plyshlog)
|
||||
, has_shrate_(has_shrate)
|
||||
, deck_(deck)
|
||||
{
|
||||
}
|
||||
|
||||
template <class GridT>
|
||||
auto SimulatorFullyImplicitBlackoilPolymer<GridT>::
|
||||
createSolver(const Wells* wells)
|
||||
-> std::unique_ptr<Solver>
|
||||
{
|
||||
typedef typename Traits::Model Model;
|
||||
|
||||
|
||||
auto model = std::unique_ptr<Model>(new Model(BaseType::model_param_,
|
||||
BaseType::grid_,
|
||||
BaseType::props_,
|
||||
BaseType::geo_,
|
||||
BaseType::rock_comp_props_,
|
||||
polymer_props_,
|
||||
wells,
|
||||
BaseType::solver_,
|
||||
BaseType::eclipse_state_,
|
||||
BaseType::has_disgas_,
|
||||
BaseType::has_vapoil_,
|
||||
has_polymer_,
|
||||
has_plyshlog_,
|
||||
has_shrate_,
|
||||
wells_rep_radius_,
|
||||
wells_perf_length_,
|
||||
wells_bore_diameter_,
|
||||
BaseType::terminal_output_));
|
||||
|
||||
if (!BaseType::threshold_pressures_by_face_.empty()) {
|
||||
model->setThresholdPressures(BaseType::threshold_pressures_by_face_);
|
||||
}
|
||||
|
||||
return std::unique_ptr<Solver>(new Solver(BaseType::solver_param_, std::move(model)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template <class GridT>
|
||||
void SimulatorFullyImplicitBlackoilPolymer<GridT>::
|
||||
handleAdditionalWellInflow(SimulatorTimer& timer,
|
||||
WellsManager& wells_manager,
|
||||
typename BaseType::WellState& well_state,
|
||||
const Wells* wells)
|
||||
{
|
||||
// compute polymer inflow
|
||||
std::unique_ptr<PolymerInflowInterface> polymer_inflow_ptr;
|
||||
if (deck_->hasKeyword("WPOLYMER")) {
|
||||
if (wells_manager.c_wells() == 0) {
|
||||
OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells.");
|
||||
}
|
||||
polymer_inflow_ptr.reset(new PolymerInflowFromDeck(deck_, BaseType::eclipse_state_, *wells, Opm::UgGridHelpers::numCells(BaseType::grid_), timer.currentStepNum()));
|
||||
} else {
|
||||
polymer_inflow_ptr.reset(new PolymerInflowBasic(0.0*Opm::unit::day,
|
||||
1.0*Opm::unit::day,
|
||||
0.0));
|
||||
}
|
||||
std::vector<double> polymer_inflow_c(Opm::UgGridHelpers::numCells(BaseType::grid_));
|
||||
polymer_inflow_ptr->getInflowValues(timer.simulationTimeElapsed(),
|
||||
timer.simulationTimeElapsed() + timer.currentStepLength(),
|
||||
polymer_inflow_c);
|
||||
well_state.polymerInflow() = polymer_inflow_c;
|
||||
|
||||
computeRepRadiusPerfLength(BaseType::eclipse_state_, timer.currentStepNum(), BaseType::grid_, wells_rep_radius_, wells_perf_length_, wells_bore_diameter_);
|
||||
}
|
||||
|
||||
|
||||
template <class GridT>
|
||||
void SimulatorFullyImplicitBlackoilPolymer<GridT>::
|
||||
setupCompressedToCartesian(const int* global_cell, int number_of_cells,
|
||||
std::map<int,int>& cartesian_to_compressed )
|
||||
{
|
||||
if (global_cell) {
|
||||
for (int i = 0; i < number_of_cells; ++i) {
|
||||
cartesian_to_compressed.insert(std::make_pair(global_cell[i], i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < number_of_cells; ++i) {
|
||||
cartesian_to_compressed.insert(std::make_pair(i, i));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
template <class GridT>
|
||||
void SimulatorFullyImplicitBlackoilPolymer<GridT>::
|
||||
computeRepRadiusPerfLength(const Opm::EclipseStateConstPtr eclipseState,
|
||||
const size_t timeStep,
|
||||
const GridT& grid,
|
||||
std::vector<double>& wells_rep_radius,
|
||||
std::vector<double>& wells_perf_length,
|
||||
std::vector<double>& wells_bore_diameter)
|
||||
{
|
||||
|
||||
// TODO, the function does not work for parallel running
|
||||
// to be fixed later.
|
||||
int number_of_cells = Opm::UgGridHelpers::numCells(grid);
|
||||
const int* global_cell = Opm::UgGridHelpers::globalCell(grid);
|
||||
const int* cart_dims = Opm::UgGridHelpers::cartDims(grid);
|
||||
auto cell_to_faces = Opm::UgGridHelpers::cell2Faces(grid);
|
||||
auto begin_face_centroids = Opm::UgGridHelpers::beginFaceCentroids(grid);
|
||||
|
||||
if (eclipseState->getSchedule()->numWells() == 0) {
|
||||
OPM_MESSAGE("No wells specified in Schedule section, "
|
||||
"initializing no wells");
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t n_perf = wells_rep_radius.size();
|
||||
|
||||
wells_rep_radius.clear();
|
||||
wells_perf_length.clear();
|
||||
wells_bore_diameter.clear();
|
||||
|
||||
wells_rep_radius.reserve(n_perf);
|
||||
wells_perf_length.reserve(n_perf);
|
||||
wells_bore_diameter.reserve(n_perf);
|
||||
|
||||
std::map<int,int> cartesian_to_compressed;
|
||||
|
||||
setupCompressedToCartesian(global_cell, number_of_cells,
|
||||
cartesian_to_compressed);
|
||||
|
||||
ScheduleConstPtr schedule = eclipseState->getSchedule();
|
||||
std::vector<WellConstPtr> wells = schedule->getWells(timeStep);
|
||||
|
||||
int well_index = 0;
|
||||
|
||||
for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) {
|
||||
WellConstPtr well = (*wellIter);
|
||||
|
||||
if (well->getStatus(timeStep) == WellCommon::SHUT) {
|
||||
continue;
|
||||
}
|
||||
{ // COMPDAT handling
|
||||
CompletionSetConstPtr completionSet = well->getCompletions(timeStep);
|
||||
for (size_t c=0; c<completionSet->size(); c++) {
|
||||
CompletionConstPtr completion = completionSet->get(c);
|
||||
if (completion->getState() == WellCompletion::OPEN) {
|
||||
int i = completion->getI();
|
||||
int j = completion->getJ();
|
||||
int k = completion->getK();
|
||||
|
||||
const int* cpgdim = cart_dims;
|
||||
int cart_grid_indx = i + cpgdim[0]*(j + cpgdim[1]*k);
|
||||
std::map<int, int>::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx);
|
||||
if (cgit == cartesian_to_compressed.end()) {
|
||||
OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << i << ' ' << j << ' '
|
||||
<< k << " not found in grid (well = " << well->name() << ')');
|
||||
}
|
||||
int cell = cgit->second;
|
||||
|
||||
{
|
||||
double radius = 0.5*completion->getDiameter();
|
||||
if (radius <= 0.0) {
|
||||
radius = 0.5*unit::feet;
|
||||
OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius);
|
||||
}
|
||||
|
||||
const std::array<double, 3> cubical =
|
||||
WellsManagerDetail::getCubeDim<3>(cell_to_faces, begin_face_centroids, cell);
|
||||
|
||||
WellCompletion::DirectionEnum direction = completion->getDirection();
|
||||
|
||||
double re; // area equivalent radius of the grid block
|
||||
double perf_length; // the length of the well perforation
|
||||
|
||||
switch (direction) {
|
||||
case Opm::WellCompletion::DirectionEnum::X:
|
||||
re = std::sqrt(cubical[1] * cubical[2] / M_PI);
|
||||
perf_length = cubical[0];
|
||||
break;
|
||||
case Opm::WellCompletion::DirectionEnum::Y:
|
||||
re = std::sqrt(cubical[0] * cubical[2] / M_PI);
|
||||
perf_length = cubical[1];
|
||||
break;
|
||||
case Opm::WellCompletion::DirectionEnum::Z:
|
||||
re = std::sqrt(cubical[0] * cubical[1] / M_PI);
|
||||
perf_length = cubical[2];
|
||||
break;
|
||||
default:
|
||||
OPM_THROW(std::runtime_error, " Dirtecion of well is not supported ");
|
||||
}
|
||||
|
||||
double repR = std::sqrt(re * radius);
|
||||
wells_rep_radius.push_back(repR);
|
||||
wells_perf_length.push_back(perf_length);
|
||||
wells_bore_diameter.push_back(2. * radius);
|
||||
}
|
||||
} else {
|
||||
if (completion->getState() != WellCompletion::SHUT) {
|
||||
OPM_THROW(std::runtime_error, "Completion state: " << WellCompletion::StateEnum2String( completion->getState() ) << " not handled");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
well_index++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Opm
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_SIMULATORFULLYIMPLICITCOMPRESSIBLEPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_SIMULATORFULLYIMPLICITCOMPRESSIBLEPOLYMER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
|
||||
#include <opm/autodiff/GeoProps.hpp>
|
||||
#include <opm/autodiff/BlackoilPropsAdInterface.hpp>
|
||||
#include <opm/autodiff/WellStateFullyImplicitBlackoil.hpp>
|
||||
#include <opm/autodiff/SimulatorBase.hpp>
|
||||
|
||||
#include <opm/polymer/fullyimplicit/FullyImplicitCompressiblePolymerSolver.hpp>
|
||||
#include <opm/polymer/fullyimplicit/BlackoilPolymerModel.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/pressure/flow_bc.h>
|
||||
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/StopWatch.hpp>
|
||||
#include <opm/core/io/eclipse/EclipseWriter.hpp>
|
||||
#include <opm/core/io/vtk/writeVtkData.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/miscUtilitiesBlackoil.hpp>
|
||||
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/core/grid/ColumnExtract.hpp>
|
||||
#include <opm/polymer/PolymerBlackoilState.hpp>
|
||||
#include <opm/polymer/PolymerInflow.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/core/transport/reorder/TransportSolverCompressibleTwophaseReorder.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/WellProductionProperties.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
template <class GridT>
|
||||
class SimulatorFullyImplicitCompressiblePolymer;
|
||||
|
||||
template <class GridT>
|
||||
struct SimulatorTraits<SimulatorFullyImplicitCompressiblePolymer<GridT> >
|
||||
{
|
||||
typedef PolymerBlackoilState ReservoirState;
|
||||
typedef WellStateFullyImplicitBlackoilPolymer WellState;
|
||||
typedef BlackoilOutputWriter OutputWriter;
|
||||
typedef GridT Grid;
|
||||
typedef FullyImplicitCompressiblePolymerSolver Solver;
|
||||
/// Dummy class, this Solver does not use a Model.
|
||||
struct Model
|
||||
{
|
||||
typedef parameter::ParameterGroup ModelParameters;
|
||||
};
|
||||
};
|
||||
|
||||
/// Class collecting all necessary components for a two-phase simulation.
|
||||
template <class GridT>
|
||||
class SimulatorFullyImplicitCompressiblePolymer
|
||||
: public SimulatorBase<SimulatorFullyImplicitCompressiblePolymer<GridT> >
|
||||
{
|
||||
typedef SimulatorFullyImplicitCompressiblePolymer ThisType;
|
||||
typedef SimulatorBase<ThisType> BaseType;
|
||||
typedef typename BaseType::Solver Solver;
|
||||
|
||||
public:
|
||||
/// Initialise from parameters and objects to observe.
|
||||
SimulatorFullyImplicitCompressiblePolymer(const parameter::ParameterGroup& param,
|
||||
const GridT& grid,
|
||||
DerivedGeology& geo,
|
||||
BlackoilPropsAdInterface& props,
|
||||
const PolymerPropsAd& polymer_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
std::shared_ptr<EclipseState> eclipse_state,
|
||||
BlackoilOutputWriter& output_writer,
|
||||
Opm::DeckConstPtr& deck,
|
||||
NewtonIterationBlackoilInterface& linsolver,
|
||||
const double* gravity);
|
||||
|
||||
std::unique_ptr<Solver> createSolver(const Wells* wells);
|
||||
|
||||
void handleAdditionalWellInflow(SimulatorTimer& timer,
|
||||
WellsManager& wells_manager,
|
||||
typename BaseType::WellState& well_state,
|
||||
const Wells* wells);
|
||||
private:
|
||||
Opm::DeckConstPtr deck_;
|
||||
const PolymerPropsAd& polymer_props_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#include "SimulatorFullyImplicitCompressiblePolymer_impl.hpp"
|
||||
|
||||
#endif // OPM_SIMULATORFULLYIMPLICITCOMPRESSIBLEPOLYMER_HEADER_INCLUDED
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef OPM_SIMULATORFULLYIMPLICITCOMPRESSIBLEPOLYMER_IMPL_HEADER_INCLUDED
|
||||
#define OPM_SIMULATORFULLYIMPLICITCOMPRESSIBLEPOLYMER_IMPL_HEADER_INCLUDED
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// Class collecting all necessary components for a two-phase simulation.
|
||||
template <class GridT>
|
||||
SimulatorFullyImplicitCompressiblePolymer<GridT>::
|
||||
SimulatorFullyImplicitCompressiblePolymer(const parameter::ParameterGroup& param,
|
||||
const GridT& grid,
|
||||
DerivedGeology& geo,
|
||||
BlackoilPropsAdInterface& props,
|
||||
const PolymerPropsAd& polymer_props,
|
||||
const RockCompressibility* rock_comp_props,
|
||||
std::shared_ptr<EclipseState> eclipse_state,
|
||||
BlackoilOutputWriter& output_writer,
|
||||
Opm::DeckConstPtr& deck,
|
||||
NewtonIterationBlackoilInterface& linsolver,
|
||||
const double* gravity)
|
||||
: BaseType(param,
|
||||
grid,
|
||||
geo,
|
||||
props,
|
||||
rock_comp_props,
|
||||
linsolver,
|
||||
gravity,
|
||||
/*disgas=*/false,
|
||||
/*vapoil=*/false,
|
||||
eclipse_state,
|
||||
output_writer,
|
||||
/*threshold_pressures_by_face=*/std::vector<double>())
|
||||
, deck_(deck)
|
||||
, polymer_props_(polymer_props)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
template <class GridT>
|
||||
auto SimulatorFullyImplicitCompressiblePolymer<GridT>::
|
||||
createSolver(const Wells* wells)
|
||||
-> std::unique_ptr<Solver>
|
||||
{
|
||||
return std::unique_ptr<Solver>(new Solver(BaseType::grid_,
|
||||
BaseType::props_,
|
||||
BaseType::geo_,
|
||||
BaseType::rock_comp_props_,
|
||||
polymer_props_,
|
||||
*wells,
|
||||
BaseType::solver_));
|
||||
}
|
||||
|
||||
template <class GridT>
|
||||
void SimulatorFullyImplicitCompressiblePolymer<GridT>::
|
||||
handleAdditionalWellInflow(SimulatorTimer& timer,
|
||||
WellsManager& wells_manager,
|
||||
typename BaseType::WellState& well_state,
|
||||
const Wells* wells)
|
||||
{
|
||||
// compute polymer inflow
|
||||
std::unique_ptr<PolymerInflowInterface> polymer_inflow_ptr;
|
||||
if (deck_->hasKeyword("WPOLYMER")) {
|
||||
if (wells_manager.c_wells() == 0) {
|
||||
OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells.");
|
||||
}
|
||||
polymer_inflow_ptr.reset(new PolymerInflowFromDeck(deck_, BaseType::eclipse_state_, *wells, Opm::UgGridHelpers::numCells(BaseType::grid_), timer.currentStepNum()));
|
||||
} else {
|
||||
polymer_inflow_ptr.reset(new PolymerInflowBasic(0.0*Opm::unit::day,
|
||||
1.0*Opm::unit::day,
|
||||
0.0));
|
||||
}
|
||||
std::vector<double> polymer_inflow_c(Opm::UgGridHelpers::numCells(BaseType::grid_));
|
||||
polymer_inflow_ptr->getInflowValues(timer.simulationTimeElapsed(),
|
||||
timer.simulationTimeElapsed() + timer.currentStepLength(),
|
||||
polymer_inflow_c);
|
||||
well_state.polymerInflow() = polymer_inflow_c;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_SIMULATORFULLYIMPLICITCOMPRESSIBLEPOLYMER_HEADER_INCLUDED
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_WELLSTATEFULLYIMPLICITBLACKOILPOLYMER_HEADER_INCLUDED
|
||||
#define OPM_WELLSTATEFULLYIMPLICITBLACKOILPOLYMER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/autodiff/WellStateFullyImplicitBlackoil.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class WellStateFullyImplicitBlackoilPolymer : public WellStateFullyImplicitBlackoil
|
||||
{
|
||||
public:
|
||||
std::vector<double>& polymerInflow() { return polymer_inflow_; }
|
||||
const std::vector<double>& polymerInflow() const { return polymer_inflow_; }
|
||||
private:
|
||||
std::vector<double> polymer_inflow_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_WELLSTATEFULLYIMPLICITBLACKOILPOLYMER_HEADER_INCLUDED
|
421
opm/polymer/polymerUtilities.cpp
Normal file
421
opm/polymer/polymerUtilities.cpp
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <opm/polymer/polymerUtilities.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// @brief Computes total mobility for a set of s/c values.
|
||||
/// @param[in] props rock and fluid properties
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] cells cells with which the saturation values are associated
|
||||
/// @param[in] s saturation values (for all phases)
|
||||
/// @param[in] c polymer concentration
|
||||
/// @param[out] totmob total mobilities.
|
||||
void computeTotalMobility(const Opm::IncompPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const std::vector<int>& cells,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& c,
|
||||
const std::vector<double>& cmax,
|
||||
std::vector<double>& totmob)
|
||||
{
|
||||
int num_cells = cells.size();
|
||||
totmob.resize(num_cells);
|
||||
std::vector<double> kr(2*num_cells);
|
||||
props.relperm(num_cells, &s[0], &cells[0], &kr[0], 0);
|
||||
const double* visc = props.viscosity();
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
double* kr_cell = &kr[2*cell];
|
||||
polyprops.effectiveTotalMobility(c[cell], cmax[cell], visc, kr_cell,
|
||||
totmob[cell]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// @brief Computes total mobility and omega for a set of s/c values.
|
||||
/// @param[in] props rock and fluid properties
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] cells cells with which the saturation values are associated
|
||||
/// @param[in] s saturation values (for all phases)
|
||||
/// @param[in] c polymer concentration
|
||||
/// @param[out] totmob total mobility
|
||||
/// @param[out] omega mobility-weighted (or fractional-flow weighted)
|
||||
/// fluid densities.
|
||||
void computeTotalMobilityOmega(const Opm::IncompPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const std::vector<int>& cells,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& c,
|
||||
const std::vector<double>& cmax,
|
||||
std::vector<double>& totmob,
|
||||
std::vector<double>& omega)
|
||||
{
|
||||
int num_cells = cells.size();
|
||||
int num_phases = props.numPhases();
|
||||
totmob.resize(num_cells);
|
||||
omega.resize(num_cells);
|
||||
assert(int(s.size()) == num_cells*num_phases);
|
||||
std::vector<double> kr(num_cells*num_phases);
|
||||
props.relperm(num_cells, &s[0], &cells[0], &kr[0], 0);
|
||||
const double* visc = props.viscosity();
|
||||
const double* rho = props.density();
|
||||
double mob[2]; // here we assume num_phases=2
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
double* kr_cell = &kr[2*cell];
|
||||
polyprops.effectiveMobilities(c[cell], cmax[cell], visc, kr_cell,
|
||||
mob);
|
||||
totmob[cell] = mob[0] + mob[1];
|
||||
omega[cell] = rho[0]*mob[0]/totmob[cell] + rho[1]*mob[1]/totmob[cell];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes the fractional flow for each cell in the cells argument
|
||||
/// @param[in] props rock and fluid properties
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] cells cells with which the saturation values are associated
|
||||
/// @param[in] s saturation values (for all phases)
|
||||
/// @param[in] c concentration values
|
||||
/// @param[in] cmax max polymer concentration experienced by cell
|
||||
/// @param[out] fractional_flow the fractional flow for each phase for each cell.
|
||||
void computeFractionalFlow(const Opm::IncompPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const std::vector<int>& cells,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& c,
|
||||
const std::vector<double>& cmax,
|
||||
std::vector<double>& fractional_flows)
|
||||
{
|
||||
int num_cells = cells.size();
|
||||
int num_phases = props.numPhases();
|
||||
if (num_phases != 2) {
|
||||
OPM_THROW(std::runtime_error, "computeFractionalFlow() assumes 2 phases.");
|
||||
}
|
||||
fractional_flows.resize(num_cells*num_phases);
|
||||
assert(int(s.size()) == num_cells*num_phases);
|
||||
std::vector<double> kr(num_cells*num_phases);
|
||||
props.relperm(num_cells, &s[0], &cells[0], &kr[0], 0);
|
||||
const double* visc = props.viscosity();
|
||||
double mob[2]; // here we assume num_phases=2
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
double* kr_cell = &kr[2*cell];
|
||||
polyprops.effectiveMobilities(c[cell], cmax[cell], visc, kr_cell, mob);
|
||||
fractional_flows[2*cell] = mob[0] / (mob[0] + mob[1]);
|
||||
fractional_flows[2*cell + 1] = mob[1] / (mob[0] + mob[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes the fractional flow for each cell in the cells argument
|
||||
/// @param[in] props rock and fluid properties
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] cells cells with which the saturation values are associated
|
||||
/// @param[in] p pressure (one value per cell)
|
||||
/// @param[in] z surface-volume values (for all P phases)
|
||||
/// @param[in] s saturation values (for all phases)
|
||||
/// @param[in] c concentration values
|
||||
/// @param[in] cmax max polymer concentration experienced by cell
|
||||
/// @param[out] fractional_flow the fractional flow for each phase for each cell.
|
||||
void computeFractionalFlow(const Opm::BlackoilPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const std::vector<int>& cells,
|
||||
const std::vector<double>& p,
|
||||
const std::vector<double>& T,
|
||||
const std::vector<double>& z,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& c,
|
||||
const std::vector<double>& cmax,
|
||||
std::vector<double>& fractional_flows)
|
||||
{
|
||||
int num_cells = cells.size();
|
||||
int num_phases = props.numPhases();
|
||||
if (num_phases != 2) {
|
||||
OPM_THROW(std::runtime_error, "computeFractionalFlow() assumes 2 phases.");
|
||||
}
|
||||
fractional_flows.resize(num_cells*num_phases);
|
||||
assert(int(s.size()) == num_cells*num_phases);
|
||||
std::vector<double> kr(num_cells*num_phases);
|
||||
props.relperm(num_cells, &s[0], &cells[0], &kr[0], 0);
|
||||
std::vector<double> mu(num_cells*num_phases);
|
||||
props.viscosity(num_cells, &p[0], &T[0], &z[0], &cells[0], &mu[0], 0);
|
||||
double mob[2]; // here we assume num_phases=2
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
double* kr_cell = &kr[2*cell];
|
||||
double* mu_cell = &mu[2*cell];
|
||||
polyprops.effectiveMobilities(c[cell], cmax[cell], mu_cell, kr_cell, mob);
|
||||
fractional_flows[2*cell] = mob[0] / (mob[0] + mob[1]);
|
||||
fractional_flows[2*cell + 1] = mob[1] / (mob[0] + mob[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @brief Computes injected and produced volumes of all phases,
|
||||
/// and injected and produced polymer mass.
|
||||
/// Note 1: assumes that only the first phase is injected.
|
||||
/// Note 2: assumes that transport has been done with an
|
||||
/// implicit method, i.e. that the current state
|
||||
/// gives the mobilities used for the preceding timestep.
|
||||
/// @param[in] props fluid and rock properties.
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] state state variables (pressure, fluxes etc.)
|
||||
/// @param[in] src if < 0: total reservoir volume outflow,
|
||||
/// if > 0: first phase reservoir volume inflow.
|
||||
/// @param[in] inj_c injected concentration by cell
|
||||
/// @param[in] dt timestep used
|
||||
/// @param[out] injected must point to a valid array with P elements,
|
||||
/// where P = s.size()/src.size().
|
||||
/// @param[out] produced must also point to a valid array with P elements.
|
||||
/// @param[out] polyinj injected mass of polymer
|
||||
/// @param[out] polyprod produced mass of polymer
|
||||
void computeInjectedProduced(const IncompPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const PolymerState& state,
|
||||
const std::vector<double>& transport_src,
|
||||
const std::vector<double>& inj_c,
|
||||
const double dt,
|
||||
double* injected,
|
||||
double* produced,
|
||||
double& polyinj,
|
||||
double& polyprod)
|
||||
{
|
||||
|
||||
const int num_cells = transport_src.size();
|
||||
if (props.numCells() != num_cells) {
|
||||
OPM_THROW(std::runtime_error, "Size of transport_src vector does not match number of cells in props.");
|
||||
}
|
||||
const int np = props.numPhases();
|
||||
if (int(state.saturation().size()) != num_cells*np) {
|
||||
OPM_THROW(std::runtime_error, "Sizes of state vectors do not match number of cells.");
|
||||
}
|
||||
const std::vector<double>& s = state.saturation();
|
||||
const std::vector<double>& c = state.concentration();
|
||||
const std::vector<double>& cmax = state.maxconcentration();
|
||||
std::fill(injected, injected + np, 0.0);
|
||||
std::fill(produced, produced + np, 0.0);
|
||||
polyinj = 0.0;
|
||||
polyprod = 0.0;
|
||||
const double* visc = props.viscosity();
|
||||
std::vector<double> kr_cell(np);
|
||||
double mob[2];
|
||||
double mc;
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
if (transport_src[cell] > 0.0) {
|
||||
injected[0] += transport_src[cell]*dt;
|
||||
polyinj += transport_src[cell]*dt*inj_c[cell];
|
||||
} else if (transport_src[cell] < 0.0) {
|
||||
const double flux = -transport_src[cell]*dt;
|
||||
const double* sat = &s[np*cell];
|
||||
props.relperm(1, sat, &cell, &kr_cell[0], 0);
|
||||
polyprops.effectiveMobilities(c[cell], cmax[cell], visc,
|
||||
&kr_cell[0], mob);
|
||||
double totmob = mob[0] + mob[1];
|
||||
for (int p = 0; p < np; ++p) {
|
||||
produced[p] += (mob[p]/totmob)*flux;
|
||||
}
|
||||
polyprops.computeMc(c[cell], mc);
|
||||
polyprod += (mob[0]/totmob)*flux*mc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Computes injected and produced volumes of all phases,
|
||||
/// and injected and produced polymer mass - in the compressible case.
|
||||
/// Note 1: assumes that only the first phase is injected.
|
||||
/// Note 2: assumes that transport has been done with an
|
||||
/// implicit method, i.e. that the current state
|
||||
/// gives the mobilities used for the preceding timestep.
|
||||
/// @param[in] props fluid and rock properties.
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] state state variables (pressure, fluxes etc.)
|
||||
/// @param[in] transport_src if < 0: total reservoir volume outflow,
|
||||
/// if > 0: first phase *surface volume* inflow.
|
||||
/// @param[in] inj_c injected concentration by cell
|
||||
/// @param[in] dt timestep used
|
||||
/// @param[out] injected must point to a valid array with P elements,
|
||||
/// where P = s.size()/transport_src.size().
|
||||
/// @param[out] produced must also point to a valid array with P elements.
|
||||
/// @param[out] polyinj injected mass of polymer
|
||||
/// @param[out] polyprod produced mass of polymer
|
||||
void computeInjectedProduced(const BlackoilPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const PolymerBlackoilState& state,
|
||||
const std::vector<double>& transport_src,
|
||||
const std::vector<double>& inj_c,
|
||||
const double dt,
|
||||
double* injected,
|
||||
double* produced,
|
||||
double& polyinj,
|
||||
double& polyprod)
|
||||
{
|
||||
const int num_cells = transport_src.size();
|
||||
if (props.numCells() != num_cells) {
|
||||
OPM_THROW(std::runtime_error, "Size of transport_src vector does not match number of cells in props.");
|
||||
}
|
||||
const int np = props.numPhases();
|
||||
if (int(state.saturation().size()) != num_cells*np) {
|
||||
OPM_THROW(std::runtime_error, "Sizes of state vectors do not match number of cells.");
|
||||
}
|
||||
const std::vector<double>& press = state.pressure();
|
||||
const std::vector<double>& temp = state.temperature();
|
||||
const std::vector<double>& s = state.saturation();
|
||||
const std::vector<double>& z = state.surfacevol();
|
||||
const std::vector<double>& c = state.concentration();
|
||||
const std::vector<double>& cmax = state.maxconcentration();
|
||||
std::fill(injected, injected + np, 0.0);
|
||||
std::fill(produced, produced + np, 0.0);
|
||||
polyinj = 0.0;
|
||||
polyprod = 0.0;
|
||||
std::vector<double> visc(np);
|
||||
std::vector<double> kr_cell(np);
|
||||
std::vector<double> mob(np);
|
||||
std::vector<double> A(np*np);
|
||||
std::vector<double> prod_resv_phase(np);
|
||||
std::vector<double> prod_surfvol(np);
|
||||
double mc;
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
if (transport_src[cell] > 0.0) {
|
||||
// Inflowing transport source is a surface volume flux
|
||||
// for the first phase.
|
||||
injected[0] += transport_src[cell]*dt;
|
||||
polyinj += transport_src[cell]*dt*inj_c[cell];
|
||||
} else if (transport_src[cell] < 0.0) {
|
||||
// Outflowing transport source is a total reservoir
|
||||
// volume flux.
|
||||
const double flux = -transport_src[cell]*dt;
|
||||
const double* sat = &s[np*cell];
|
||||
props.relperm(1, sat, &cell, &kr_cell[0], 0);
|
||||
props.viscosity(1, &press[cell], &temp[cell], &z[np*cell], &cell, &visc[0], 0);
|
||||
props.matrix(1, &press[cell], &temp[cell], &z[np*cell], &cell, &A[0], 0);
|
||||
polyprops.effectiveMobilities(c[cell], cmax[cell], &visc[0],
|
||||
&kr_cell[0], &mob[0]);
|
||||
double totmob = 0.0;
|
||||
for (int p = 0; p < np; ++p) {
|
||||
totmob += mob[p];
|
||||
}
|
||||
std::fill(prod_surfvol.begin(), prod_surfvol.end(), 0.0);
|
||||
for (int p = 0; p < np; ++p) {
|
||||
prod_resv_phase[p] = (mob[p]/totmob)*flux;
|
||||
for (int q = 0; q < np; ++q) {
|
||||
prod_surfvol[q] += prod_resv_phase[p]*A[q + np*p];
|
||||
}
|
||||
}
|
||||
for (int p = 0; p < np; ++p) {
|
||||
produced[p] += prod_surfvol[p];
|
||||
}
|
||||
polyprops.computeMc(c[cell], mc);
|
||||
polyprod += produced[0]*mc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// @brief Computes total polymer mass over all grid cells.
|
||||
/// @param[in] pv the pore volume by cell.
|
||||
/// @param[in] s saturation values (for all P phases)
|
||||
/// @param[in] c polymer concentration
|
||||
/// @param[in] dps dead pore space
|
||||
/// @return total polymer mass in grid.
|
||||
double computePolymerMass(const std::vector<double>& pv,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& c,
|
||||
const double dps)
|
||||
{
|
||||
const int num_cells = pv.size();
|
||||
const int np = s.size()/pv.size();
|
||||
if (int(s.size()) != num_cells*np) {
|
||||
OPM_THROW(std::runtime_error, "Sizes of s and pv vectors do not match.");
|
||||
}
|
||||
double polymass = 0.0;
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
polymass += c[cell]*s[np*cell + 0]*pv[cell]*(1 - dps);
|
||||
}
|
||||
return polymass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Computes total absorbed polymer mass over all grid cells.
|
||||
/// @param[in] props fluid and rock properties.
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] pv the pore volume by cell.
|
||||
/// @param[in] cmax max polymer concentration for cell
|
||||
/// @return total absorbed polymer mass.
|
||||
double computePolymerAdsorbed(const IncompPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const std::vector<double>& pv,
|
||||
const std::vector<double>& cmax)
|
||||
{
|
||||
const int num_cells = pv.size();
|
||||
const double rhor = polyprops.rockDensity();
|
||||
const double* poro = props.porosity();
|
||||
double abs_mass = 0.0;
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
double c_ads;
|
||||
polyprops.simpleAdsorption(cmax[cell], c_ads);
|
||||
abs_mass += c_ads*pv[cell]*((1.0 - poro[cell])/poro[cell])*rhor;
|
||||
}
|
||||
return abs_mass;
|
||||
}
|
||||
|
||||
/// @brief Computes total absorbed polymer mass over all grid cells.
|
||||
/// With compressibility
|
||||
/// @param[in] grid grid
|
||||
/// @param[in] props fluid and rock properties.
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] state fluid state variable
|
||||
/// @param[in] rock_comp rock compressibility (depends on pressure)
|
||||
/// @return total absorbed polymer mass.
|
||||
double computePolymerAdsorbed(const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const PolymerBlackoilState& state,
|
||||
const RockCompressibility* rock_comp
|
||||
)
|
||||
{
|
||||
const int num_cells = props.numCells();
|
||||
const double rhor = polyprops.rockDensity();
|
||||
std::vector<double> porosity;
|
||||
if (rock_comp && rock_comp->isActive()) {
|
||||
computePorosity(grid, props.porosity(), *rock_comp, state.pressure(), porosity);
|
||||
} else {
|
||||
porosity.assign(props.porosity(), props.porosity() + num_cells);
|
||||
}
|
||||
double abs_mass = 0.0;
|
||||
const std::vector<double>& cmax = state.maxconcentration();
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
double c_ads;
|
||||
polyprops.simpleAdsorption(cmax[cell], c_ads);
|
||||
abs_mass += c_ads*grid.cell_volumes[cell]*(1.0 - porosity[cell])*rhor;
|
||||
}
|
||||
return abs_mass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
||||
|
210
opm/polymer/polymerUtilities.hpp
Normal file
210
opm/polymer/polymerUtilities.hpp
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_POLYMERUTILITIES_HEADER_INCLUDED
|
||||
#define OPM_POLYMERUTILITIES_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/props/IncompPropertiesInterface.hpp>
|
||||
#include <opm/core/props/BlackoilPropertiesInterface.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
#include <opm/polymer/PolymerState.hpp>
|
||||
#include <opm/polymer/PolymerBlackoilState.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
#include <opm/core/utility/SparseVector.hpp>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// @brief Computes total mobility for a set of s/c values.
|
||||
/// @param[in] props rock and fluid properties
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] cells cells with which the saturation values are associated
|
||||
/// @param[in] s saturation values (for all phases)
|
||||
/// @param[in] c polymer concentration
|
||||
/// @param[out] totmob total mobilities.
|
||||
void computeTotalMobility(const Opm::IncompPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const std::vector<int>& cells,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& c,
|
||||
const std::vector<double>& cmax,
|
||||
std::vector<double>& totmob);
|
||||
|
||||
/// @brief Computes total mobility and omega for a set of s/c values.
|
||||
/// @param[in] props rock and fluid properties
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] cells cells with which the saturation values are associated
|
||||
/// @param[in] s saturation values (for all phases)
|
||||
/// @param[in] c polymer concentration
|
||||
/// @param[in] cmax max polymer concentration experienced by cell
|
||||
/// @param[out] totmob total mobility
|
||||
/// @param[out] omega mobility-weighted (or fractional-flow weighted)
|
||||
/// fluid densities.
|
||||
void computeTotalMobilityOmega(const Opm::IncompPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const std::vector<int>& cells,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& c,
|
||||
const std::vector<double>& cmax,
|
||||
std::vector<double>& totmob,
|
||||
std::vector<double>& omega);
|
||||
|
||||
/// Computes the fractional flow for each cell in the cells argument
|
||||
/// @param[in] props rock and fluid properties
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] cells cells with which the saturation values are associated
|
||||
/// @param[in] s saturation values (for all phases)
|
||||
/// @param[in] c concentration values
|
||||
/// @param[in] cmax max polymer concentration experienced by cell
|
||||
/// @param[out] fractional_flow the fractional flow for each phase for each cell.
|
||||
void computeFractionalFlow(const Opm::IncompPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const std::vector<int>& cells,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& c,
|
||||
const std::vector<double>& cmax,
|
||||
std::vector<double>& fractional_flows);
|
||||
|
||||
/// Computes the fractional flow for each cell in the cells argument
|
||||
/// @param[in] props rock and fluid properties
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] cells cells with which the saturation values are associated
|
||||
/// @param[in] p pressure (one value per cell)
|
||||
/// @param[in] T temperature (one value per cell)
|
||||
/// @param[in] z surface-volume values (for all P phases)
|
||||
/// @param[in] s saturation values (for all phases)
|
||||
/// @param[in] c concentration values
|
||||
/// @param[in] cmax max polymer concentration experienced by cell
|
||||
/// @param[out] fractional_flow the fractional flow for each phase for each cell.
|
||||
void computeFractionalFlow(const Opm::BlackoilPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const std::vector<int>& cells,
|
||||
const std::vector<double>& p,
|
||||
const std::vector<double>& T,
|
||||
const std::vector<double>& z,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& c,
|
||||
const std::vector<double>& cmax,
|
||||
std::vector<double>& fractional_flows);
|
||||
|
||||
/// @brief Computes injected and produced volumes of all phases,
|
||||
/// and injected and produced polymer mass.
|
||||
/// Note 1: assumes that only the first phase is injected.
|
||||
/// Note 2: assumes that transport has been done with an
|
||||
/// implicit method, i.e. that the current state
|
||||
/// gives the mobilities used for the preceding timestep.
|
||||
/// @param[in] props fluid and rock properties.
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] state state variables (pressure, fluxes etc.)
|
||||
/// @param[in] src if < 0: total reservoir volume outflow,
|
||||
/// if > 0: first phase reservoir volume inflow.
|
||||
/// @param[in] inj_c injected concentration by cell
|
||||
/// @param[in] dt timestep used
|
||||
/// @param[out] injected must point to a valid array with P elements,
|
||||
/// where P = s.size()/src.size().
|
||||
/// @param[out] produced must also point to a valid array with P elements.
|
||||
/// @param[out] polyinj injected mass of polymer
|
||||
/// @param[out] polyprod produced mass of polymer
|
||||
void computeInjectedProduced(const IncompPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const PolymerState& state,
|
||||
const std::vector<double>& transport_src,
|
||||
const std::vector<double>& inj_c,
|
||||
const double dt,
|
||||
double* injected,
|
||||
double* produced,
|
||||
double& polyinj,
|
||||
double& polyprod);
|
||||
|
||||
/// @brief Computes injected and produced volumes of all phases,
|
||||
/// and injected and produced polymer mass - in the compressible case.
|
||||
/// Note 1: assumes that only the first phase is injected.
|
||||
/// Note 2: assumes that transport has been done with an
|
||||
/// implicit method, i.e. that the current state
|
||||
/// gives the mobilities used for the preceding timestep.
|
||||
/// @param[in] props fluid and rock properties.
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] state state variables (pressure, fluxes etc.)
|
||||
/// @param[in] src if < 0: total reservoir volume outflow,
|
||||
/// if > 0: first phase *surface volume* inflow.
|
||||
/// @param[in] inj_c injected concentration by cell
|
||||
/// @param[in] dt timestep used
|
||||
/// @param[out] injected must point to a valid array with P elements,
|
||||
/// where P = s.size()/src.size().
|
||||
/// @param[out] produced must also point to a valid array with P elements.
|
||||
/// @param[out] polyinj injected mass of polymer
|
||||
/// @param[out] polyprod produced mass of polymer
|
||||
void computeInjectedProduced(const BlackoilPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const PolymerBlackoilState& state,
|
||||
const std::vector<double>& transport_src,
|
||||
const std::vector<double>& inj_c,
|
||||
const double dt,
|
||||
double* injected,
|
||||
double* produced,
|
||||
double& polyinj,
|
||||
double& polyprod);
|
||||
|
||||
/// @brief Computes total (free) polymer mass over all grid cells.
|
||||
/// @param[in] pv the pore volume by cell.
|
||||
/// @param[in] s saturation values (for all P phases)
|
||||
/// @param[in] c polymer concentration
|
||||
/// @param[in] dps dead pore space
|
||||
/// @return total polymer mass in grid.
|
||||
double computePolymerMass(const std::vector<double>& pv,
|
||||
const std::vector<double>& s,
|
||||
const std::vector<double>& c,
|
||||
const double dps);
|
||||
|
||||
/// @brief Computes total absorbed polymer mass over all grid cells.
|
||||
/// @param[in] props fluid and rock properties.
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] pv the pore volume by cell.
|
||||
/// @param[in] cmax max polymer concentration for cell
|
||||
/// @return total absorbed polymer mass.
|
||||
double computePolymerAdsorbed(const IncompPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const std::vector<double>& pv,
|
||||
const std::vector<double>& cmax);
|
||||
|
||||
/// @brief Computes total absorbed polymer mass over all grid cells.
|
||||
/// With compressibility
|
||||
/// @param[in] grid grid
|
||||
/// @param[in] props fluid and rock properties.
|
||||
/// @param[in] polyprops polymer properties
|
||||
/// @param[in] state State variables
|
||||
/// @param[in] rock_comp Rock compressibility (optional)
|
||||
/// @return total absorbed polymer mass.
|
||||
double computePolymerAdsorbed(const UnstructuredGrid& grid,
|
||||
const BlackoilPropertiesInterface& props,
|
||||
const Opm::PolymerProperties& polyprops,
|
||||
const PolymerBlackoilState& state,
|
||||
const RockCompressibility* rock_comp);
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
#endif // OPM_POLYMERUTILITIES_HEADER_INCLUDED
|
259
tests/test_singlecellsolves.cpp
Normal file
259
tests/test_singlecellsolves.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/core/pressure/FlowBCManager.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/simulator/initState.hpp>
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <opm/core/props/IncompPropertiesBasic.hpp>
|
||||
#include <opm/core/props/IncompPropertiesFromDeck.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/core/linalg/LinearSolverFactory.hpp>
|
||||
|
||||
#include <opm/polymer/PolymerState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/polymer/IncompTpfaPolymer.hpp>
|
||||
#include <opm/polymer/TransportSolverTwophasePolymer.hpp>
|
||||
#include <opm/polymer/PolymerProperties.hpp>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
|
||||
|
||||
// ----------------- Main program -----------------
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
using namespace Opm;
|
||||
|
||||
// std::cout << "\n================ Test program for single-cell solves with polymer ===============\n\n";
|
||||
parameter::ParameterGroup param(argc, argv, false);
|
||||
param.disableOutput();
|
||||
// std::cout << "--------------- Reading parameters ---------------" << std::endl;
|
||||
|
||||
boost::scoped_ptr<GridManager> grid;
|
||||
boost::scoped_ptr<IncompPropertiesInterface> props;
|
||||
PolymerState state;
|
||||
Opm::PolymerProperties poly_props;
|
||||
// bool check_well_controls = false;
|
||||
// int max_well_control_iterations = 0;
|
||||
|
||||
// -------- Initialising section ----------
|
||||
|
||||
// Grid init.
|
||||
grid.reset(new GridManager(2, 1, 1, 1.0, 1.0, 1.0));
|
||||
// Rock and fluid init.
|
||||
props.reset(new IncompPropertiesBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells));
|
||||
// Init state variables (saturation and pressure).
|
||||
initStateBasic(*grid->c_grid(), *props, param, 0.0, state);
|
||||
// Init Polymer state
|
||||
if (param.has("poly_init")) {
|
||||
double poly_init = param.getDefault("poly_init", 0.0);
|
||||
for (int cell = 0; cell < grid->c_grid()->number_of_cells; ++cell) {
|
||||
double smin[2], smax[2];
|
||||
props->satRange(1, &cell, smin, smax);
|
||||
if (state.saturation()[2*cell] > 0.5*(smin[0] + smax[0])) {
|
||||
state.concentration()[cell] = poly_init;
|
||||
state.maxconcentration()[cell] = poly_init;
|
||||
} else {
|
||||
state.saturation()[2*cell + 0] = 0.;
|
||||
state.saturation()[2*cell + 1] = 1.;
|
||||
state.concentration()[cell] = 0.;
|
||||
state.maxconcentration()[cell] = 0.;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Init polymer properties.
|
||||
// Setting defaults to provide a simple example case.
|
||||
double c_max = param.getDefault("c_max_limit", 5.0);
|
||||
double mix_param = param.getDefault("mix_param", 1.0);
|
||||
double rock_density = param.getDefault("rock_density", 1000.0);
|
||||
double dead_pore_vol = param.getDefault("dead_pore_vol", 0.0); // Note that we default to no dps here!
|
||||
double res_factor = param.getDefault("res_factor", 1.) ; // res_factor = 1 gives no change in permeability
|
||||
double c_max_ads = param.getDefault("c_max_ads", 1.);
|
||||
int ads_index = param.getDefault<int>("ads_index", Opm::PolymerProperties::NoDesorption);
|
||||
std::vector<double> c_vals_visc(2, -1e100);
|
||||
c_vals_visc[0] = 0.0;
|
||||
c_vals_visc[1] = 7.0;
|
||||
std::vector<double> visc_mult_vals(2, -1e100);
|
||||
visc_mult_vals[0] = 1.0;
|
||||
// poly_props.visc_mult_vals[1] = param.getDefault("c_max_viscmult", 30.0);
|
||||
visc_mult_vals[1] = 20.0;
|
||||
std::vector<double> c_vals_ads(3, -1e100);
|
||||
c_vals_ads[0] = 0.0;
|
||||
c_vals_ads[1] = 2.0;
|
||||
c_vals_ads[2] = 8.0;
|
||||
std::vector<double> ads_vals(3, -1e100);
|
||||
ads_vals[0] = 0.0;
|
||||
ads_vals[1] = 0.0015;
|
||||
ads_vals[2] = 0.0025;
|
||||
// ads_vals[1] = 0.0;
|
||||
// ads_vals[2] = 0.0;
|
||||
std::vector<double> water_vel_vals(2, -1e100);
|
||||
water_vel_vals[0] = 0.0;
|
||||
water_vel_vals[1] = 10.0;
|
||||
std::vector<double> shear_vrf_vals(2, -1e100);
|
||||
shear_vrf_vals[0] = 1.0;
|
||||
shear_vrf_vals[1] = 1.0;
|
||||
poly_props.set(c_max, mix_param, rock_density, dead_pore_vol, res_factor, c_max_ads,
|
||||
static_cast<Opm::PolymerProperties::AdsorptionBehaviour>(ads_index),
|
||||
c_vals_visc, visc_mult_vals, c_vals_ads, ads_vals, water_vel_vals, shear_vrf_vals);
|
||||
|
||||
// Initialising src
|
||||
int num_cells = grid->c_grid()->number_of_cells;
|
||||
std::vector<double> src(num_cells, 0.0);
|
||||
// Compute pore volumes, in order to enable specifying injection rate
|
||||
// terms of total pore volume.
|
||||
std::vector<double> porevol;
|
||||
computePorevolume(*grid->c_grid(), props->porosity(), porevol);
|
||||
const double default_injection = 1.0;
|
||||
const double flow_per_sec = param.getDefault<double>("injected_porevolumes_per_sec", default_injection)
|
||||
*porevol[0];
|
||||
src[0] = flow_per_sec;
|
||||
src[num_cells - 1] = -flow_per_sec;
|
||||
|
||||
// Boundary conditions.
|
||||
FlowBCManager bcs;
|
||||
|
||||
// Linear solver.
|
||||
LinearSolverFactory linsolver(param);
|
||||
|
||||
// Reordering solver.
|
||||
const double nl_tolerance = param.getDefault("nl_tolerance", 1e-9);
|
||||
const int nl_maxiter = param.getDefault("nl_maxiter", 30);
|
||||
Opm::TransportSolverTwophasePolymer::SingleCellMethod method;
|
||||
std::string method_string = param.getDefault("single_cell_method", std::string("Bracketing"));
|
||||
if (method_string == "Bracketing") {
|
||||
method = Opm::TransportSolverTwophasePolymer::Bracketing;
|
||||
} else if (method_string == "Newton") {
|
||||
method = Opm::TransportSolverTwophasePolymer::Newton;
|
||||
} else if (method_string == "Gradient") {
|
||||
method = Opm::TransportSolverTwophasePolymer::Gradient;
|
||||
} else if (method_string == "NewtonSimpleSC") {
|
||||
method = Opm::TransportSolverTwophasePolymer::NewtonSimpleSC;
|
||||
} else if (method_string == "NewtonSimpleC") {
|
||||
method = Opm::TransportSolverTwophasePolymer::NewtonSimpleC;
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, "Unknown method: " << method_string);
|
||||
}
|
||||
Opm::TransportSolverTwophasePolymer reorder_model(*grid->c_grid(), *props, poly_props,
|
||||
method, nl_tolerance, nl_maxiter);
|
||||
|
||||
// Warn if any parameters are unused.
|
||||
// if (param.anyUnused()) {
|
||||
// std::cout << "-------------------- Unused parameters: --------------------\n";
|
||||
// param.displayUsage();
|
||||
// std::cout << "----------------------------------------------------------------" << std::endl;
|
||||
// }
|
||||
|
||||
// Write parameters to file for later reference.
|
||||
param.writeParam("test_singlecellsolves.param");
|
||||
|
||||
// Setting up a number of input (s, c) pairs and solving.
|
||||
// HACK warning: we manipulate the source term,
|
||||
// but the compressibility term in the solver
|
||||
// assumes that all inflow is water inflow. Therefore
|
||||
// one must zero the compressibility term in
|
||||
// TransportSolverTwophasePolymer line 365 before compiling this program.
|
||||
// (To fix this we should add proper all-phase src terms.)
|
||||
std::vector<double> transport_src = src;
|
||||
const double dt = param.getDefault("dt", 1.0);
|
||||
const int num_sats = 501;
|
||||
const int num_concs = 501;
|
||||
// Find the face between cell 0 and 1...
|
||||
const UnstructuredGrid& ug = *grid->c_grid();
|
||||
int face01 = -1;
|
||||
for (int f = 0; f < ug.number_of_faces; ++f) {
|
||||
if (ug.face_cells[2*f] == 0 && ug.face_cells[2*f+1] == 1) {
|
||||
face01 = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (face01 == -1) {
|
||||
OPM_THROW(std::runtime_error, "Could not find face adjacent to cells [0 1]");
|
||||
}
|
||||
state.faceflux()[face01] = src[0];
|
||||
for (int sats = 0; sats < num_sats; ++sats) {
|
||||
const double s = double(sats)/double(num_sats - 1);
|
||||
const double ff = s; // Simplified a lot...
|
||||
for (int conc = 0; conc < num_concs; ++conc) {
|
||||
const double c = poly_props.cMax()*double(conc)/double(num_concs - 1);
|
||||
std::vector<double> polymer_inflow_c(num_cells, c);
|
||||
// std::cout << "(s, c) = (" << s << ", " << c << ")\n";
|
||||
transport_src[0] = src[0]*ff;
|
||||
// Resetting the state for next run.
|
||||
state.saturation()[0] = 0.0;
|
||||
state.saturation()[1] = 0.0;
|
||||
state.concentration()[0] = 0.0;
|
||||
state.concentration()[1] = 0.0;
|
||||
state.maxconcentration()[0] = 0.0;
|
||||
state.maxconcentration()[1] = 0.0;
|
||||
reorder_model.solve(&state.faceflux()[0],
|
||||
&porevol[0],
|
||||
&transport_src[0],
|
||||
&polymer_inflow_c[0],
|
||||
dt,
|
||||
state.saturation(),
|
||||
state.concentration(),
|
||||
state.maxconcentration());
|
||||
|
||||
#ifdef PROFILING
|
||||
// Extract residual counts.
|
||||
typedef std::list<Opm::TransportSolverTwophasePolymer::Newton_Iter> ListRes;
|
||||
const ListRes& res_counts = reorder_model.res_counts;
|
||||
double counts[2] = { 0, 0 };
|
||||
for (ListRes::const_iterator it = res_counts.begin(); it != res_counts.end(); ++it) {
|
||||
if (it->cell == 0) {
|
||||
++counts[it->res_s];
|
||||
}
|
||||
}
|
||||
// std::cout << "c residual count: " << counts[0] << '\n';
|
||||
// std::cout << "s residual count: " << counts[1] << '\n';
|
||||
std::cout << counts[0] << ' ' << counts[1] << ' ' << s << ' ' << c << '\n';
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
std::cerr << "Program threw an exception: " << e.what() << "\n";
|
||||
throw;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user