code is now included using \snippet. Apparently this looks better with the new Doxygen version. The HTML_EXTRA_STYLESHEET is now used rather then the HTML_STYLESHEET in order to include used-defined styles for the same reason
This commit is contained in:
@@ -46,18 +46,22 @@
|
||||
#include <opm/core/wells/WellCollection.hpp>
|
||||
|
||||
/// \page tutorial4 Well controls
|
||||
|
||||
/// This tutorial explains how to construct an example with wells
|
||||
/// \page tutorial4
|
||||
/// \section commentedsource1 Program walk-through.
|
||||
/// \details
|
||||
/// Main function
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp main
|
||||
/// \internal[main]
|
||||
int main ()
|
||||
{
|
||||
/// \endcode
|
||||
/// \internal[main]
|
||||
/// \endinternal
|
||||
/// \page tutorial4
|
||||
/// \details
|
||||
/// We define the grid. A cartesian grid with 1200 cells.
|
||||
/// \code
|
||||
/// We define the grid. A Cartesian grid with 1200 cells.
|
||||
/// \snippet tutorial4.cpp cartesian grid
|
||||
/// \internal[cartesian grid]
|
||||
int dim = 3;
|
||||
int nx = 20;
|
||||
int ny = 20;
|
||||
@@ -69,126 +73,162 @@ int main ()
|
||||
GridManager grid_manager(nx, ny, nz, dx, dy, dz);
|
||||
const UnstructuredGrid& grid = *grid_manager.c_grid();
|
||||
int num_cells = grid.number_of_cells;
|
||||
/// \endcode
|
||||
/// \internal[cartesian grid]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details
|
||||
/// We define the properties of the fluid.\n
|
||||
/// Number of phases.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp Number of phases
|
||||
/// \internal[Number of phases]
|
||||
int num_phases = 2;
|
||||
using namespace unit;
|
||||
using namespace prefix;
|
||||
/// \endcode
|
||||
/// \internal[Number of phases]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details density vector (one component per phase).
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp density
|
||||
/// \internal[density]
|
||||
std::vector<double> rho(2, 1000.);
|
||||
/// \endcode
|
||||
/// \internal[density]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details viscosity vector (one component per phase).
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp viscosity
|
||||
/// \internal[viscosity]
|
||||
std::vector<double> mu(2, 1.*centi*Poise);
|
||||
/// \endcode
|
||||
/// \internal[viscosity]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details porosity and permeability of the rock.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp rock
|
||||
/// \internal[rock]
|
||||
double porosity = 0.5;
|
||||
double k = 10*milli*darcy;
|
||||
/// \endcode
|
||||
/// \internal[rock]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We define the relative permeability function. We use a basic fluid
|
||||
/// description and set this function to be linear. For more realistic fluid, the
|
||||
/// saturation function is given by the data.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp relative permeability
|
||||
/// \internal[relative permeability]
|
||||
SaturationPropsBasic::RelPermFunc rel_perm_func = SaturationPropsBasic::Linear;
|
||||
/// \endcode
|
||||
/// \internal[relative permeability]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We construct a basic fluid with the properties we have defined above.
|
||||
/// Each property is constant and hold for all cells.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp fluid properties
|
||||
/// \internal[fluid properties]
|
||||
IncompPropertiesBasic props(num_phases, rel_perm_func, rho, mu,
|
||||
porosity, k, dim, num_cells);
|
||||
/// \endcode
|
||||
/// \internal[fluid properties]
|
||||
/// \endinternal
|
||||
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Gravity parameters. Here, we set zero gravity.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp Gravity
|
||||
/// \internal[Gravity]
|
||||
const double *grav = 0;
|
||||
std::vector<double> omega;
|
||||
/// \endcode
|
||||
/// \internal[Gravity]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We set up the source term. Positive numbers indicate that the cell is a source,
|
||||
/// while negative numbers indicate a sink.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp source
|
||||
/// \internal[source]
|
||||
std::vector<double> src(num_cells, 0.0);
|
||||
src[0] = 1.;
|
||||
src[num_cells-1] = -1.;
|
||||
/// \endcode
|
||||
/// \internal[source]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We compute the pore volume
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp pore volume
|
||||
/// \internal[pore volume]
|
||||
std::vector<double> porevol;
|
||||
Opm::computePorevolume(grid, props.porosity(), porevol);
|
||||
/// \endcode
|
||||
/// \internal[pore volume]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Set up the transport solver. This is a reordering implicit Euler transport solver.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp transport solver
|
||||
/// \internal[transport solver]
|
||||
const double tolerance = 1e-9;
|
||||
const int max_iterations = 30;
|
||||
Opm::TransportModelTwophase transport_solver(grid, props, tolerance, max_iterations);
|
||||
/// \endcode
|
||||
/// \internal[transport solver]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Time integration parameters
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp Time integration
|
||||
/// \internal[Time integration]
|
||||
double dt = 0.1*day;
|
||||
int num_time_steps = 20;
|
||||
/// \endcode
|
||||
/// \internal[Time integration]
|
||||
/// \endinternal
|
||||
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We define a vector which contains all cell indexes. We use this
|
||||
/// vector to set up parameters on the whole domains.
|
||||
/// \snippet tutorial4.cpp cell indexes
|
||||
/// \internal[cell indexes]
|
||||
std::vector<int> allcells(num_cells);
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
allcells[cell] = cell;
|
||||
}
|
||||
/// \internal[cell indexes]
|
||||
/// \endinternal
|
||||
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We set up the boundary conditions. Letting bcs empty is equivalent
|
||||
/// to no flow boundary conditions.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp boundary
|
||||
/// \internal[boundary]
|
||||
FlowBCManager bcs;
|
||||
/// \endcode
|
||||
/// \internal[boundary]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details
|
||||
/// We set up a two-phase state object, and
|
||||
/// initialise water saturation to minimum everywhere.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp two-phase state
|
||||
/// \internal[two-phase state]
|
||||
TwophaseState state;
|
||||
state.init(grid, 2);
|
||||
state.setFirstSat(allcells, props, TwophaseState::MinSat);
|
||||
/// \endcode
|
||||
/// \internal[two-phase state]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details This string will contain the name of a VTK output vector.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp VTK output
|
||||
/// \internal[VTK output]
|
||||
std::ostringstream vtkfilename;
|
||||
/// \endcode
|
||||
|
||||
/// \internal[VTK output]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// To create wells we need an instance of the PhaseUsage-object
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp PhaseUsage-object
|
||||
/// \internal[PhaseUsage-object]
|
||||
PhaseUsage phase_usage;
|
||||
phase_usage.num_phases = num_phases;
|
||||
phase_usage.phase_used[BlackoilPhases::Aqua] = 1;
|
||||
@@ -197,44 +237,54 @@ int main ()
|
||||
|
||||
phase_usage.phase_pos[BlackoilPhases::Aqua] = 0;
|
||||
phase_usage.phase_pos[BlackoilPhases::Liquid] = 1;
|
||||
/// \endcode
|
||||
/// \internal[PhaseUsage-object]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details This will contain our well-specific information
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp well_collection
|
||||
/// \internal[well_collection]
|
||||
WellCollection well_collection;
|
||||
/// \endcode
|
||||
/// \internal[well_collection]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Create the production specification for our top well group.
|
||||
/// We set a target limit for total reservoir rate, and set the controlling
|
||||
/// mode of the group to be controlled by the reservoir rate.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp production specification
|
||||
/// \internal[production specification]
|
||||
ProductionSpecification well_group_prod_spec;
|
||||
well_group_prod_spec.reservoir_flow_max_rate_ = 0.1;
|
||||
well_group_prod_spec.control_mode_ = ProductionSpecification::RESV;
|
||||
/// \endcode
|
||||
/// \internal[production specification]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Create our well group. We hand it an empty injection specification,
|
||||
/// as we don't want to control its injection target. We use the shared_ptr-type because that's
|
||||
/// what the interface expects. The first argument is the (unique) name of the group.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp injection specification
|
||||
/// \internal[injection specification]
|
||||
std::tr1::shared_ptr<WellsGroupInterface> well_group(new WellsGroup("group", well_group_prod_spec, InjectionSpecification(),
|
||||
phase_usage));
|
||||
/// \endcode
|
||||
/// \internal[injection specification]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We add our well_group to the well_collection
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp well_collection
|
||||
/// \internal[well_collection]
|
||||
well_collection.addChild(well_group);
|
||||
/// \endcode
|
||||
/// \internal[well_collection]
|
||||
/// \endinternal
|
||||
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Create the production specification and Well objects (total 4 wells). We set all our wells to be group controlled.
|
||||
/// We pass in the string argument \C "group" to set the parent group.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp create well objects
|
||||
/// \internal[create well objects]
|
||||
const int num_wells = 4;
|
||||
for (int i = 0; i < num_wells; ++i) {
|
||||
std::stringstream well_name;
|
||||
@@ -246,20 +296,24 @@ int main ()
|
||||
well_collection.addChild(well_leaf_node, "group");
|
||||
|
||||
}
|
||||
/// \endcode
|
||||
/// \internal[create well objects]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Now we create the C struct to hold our wells (this is to interface with the solver code). For now we
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp well struct
|
||||
/// \internal[well struct]
|
||||
Wells* wells = create_wells(num_phases, num_wells, num_wells /*number of perforations. We'll only have one perforation per well*/);
|
||||
/// \endcode
|
||||
/// \internal[well struct]
|
||||
/// \endinternal
|
||||
|
||||
///
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We need to add each well to the C API.
|
||||
/// To do this we need to specify the relevant cells the well will be located in (\C well_cells).
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp well cells
|
||||
/// \internal[well cells]
|
||||
for (int i = 0; i < num_wells; ++i) {
|
||||
const int well_cells = i*nx;
|
||||
const double well_index = 1;
|
||||
@@ -268,87 +322,112 @@ int main ()
|
||||
add_well(PRODUCER, 0, 1, NULL, &well_cells, &well_index,
|
||||
well_name.str().c_str(), wells);
|
||||
}
|
||||
/// \endcode
|
||||
/// \internal[well cells]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We need to make the well collection aware of our wells object
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp set well pointer
|
||||
/// \internal[set well pointer]
|
||||
well_collection.setWellsPointer(wells);
|
||||
/// \endcode
|
||||
/// \internal[set well pointer]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// We're not using well controls, just group controls, so we need to apply them.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp apply group controls
|
||||
/// \internal[apply group controls]
|
||||
well_collection.applyGroupControls();
|
||||
///\endcode
|
||||
/// \internal[apply group controls]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We set up necessary information for the wells
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp init wells
|
||||
/// \internal[init wells]
|
||||
WellState well_state;
|
||||
well_state.init(wells, state);
|
||||
std::vector<double> well_resflowrates_phase;
|
||||
std::vector<double> well_surflowrates_phase;
|
||||
std::vector<double> fractional_flows;
|
||||
/// \endcode
|
||||
/// \internal[init wells]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We set up the pressure solver.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp pressure solver
|
||||
/// \internal[pressure solver]
|
||||
LinearSolverUmfpack linsolver;
|
||||
IncompTpfa psolver(grid, props, linsolver,
|
||||
grav, wells, src, bcs.c_bcs());
|
||||
/// \endcode
|
||||
/// \internal[pressure solver]
|
||||
/// \endinternal
|
||||
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Loop over the time steps.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp time loop
|
||||
/// \internal[time loop]
|
||||
for (int i = 0; i < num_time_steps; ++i) {
|
||||
/// \endcode
|
||||
/// \internal[time loop]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We're solving the pressure until the well conditions are met
|
||||
/// or until we reach the maximum number of iterations.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp well iterations
|
||||
/// \internal[well iterations]
|
||||
const int max_well_iterations = 10;
|
||||
int well_iter = 0;
|
||||
bool well_conditions_met = false;
|
||||
while (!well_conditions_met) {
|
||||
/// \endcode
|
||||
/// \internal[well iterations]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Solve the pressure equation
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp pressure solve
|
||||
/// \internal[pressure solve]
|
||||
psolver.solve(dt, state, well_state);
|
||||
|
||||
/// \endcode
|
||||
/// \internal[pressure solve]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We compute the new well rates. Notice that we approximate (wrongly) surfflowsrates := resflowsrate
|
||||
/// \snippet tutorial4.cpp compute well rates
|
||||
/// \internal[compute well rates]
|
||||
Opm::computeFractionalFlow(props, allcells, state.saturation(), fractional_flows);
|
||||
Opm::computePhaseFlowRatesPerWell(*wells, well_state.perfRates(), fractional_flows, well_resflowrates_phase);
|
||||
Opm::computePhaseFlowRatesPerWell(*wells, well_state.perfRates(), fractional_flows, well_surflowrates_phase);
|
||||
/// \endcode
|
||||
/// \internal[compute well rates]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details We check if the well conditions are met.
|
||||
/// \snippet tutorial4.cpp check well conditions
|
||||
/// \internal[check well conditions]
|
||||
well_conditions_met = well_collection.conditionsMet(well_state.bhp(), well_resflowrates_phase, well_surflowrates_phase);
|
||||
++well_iter;
|
||||
if (!well_conditions_met && well_iter == max_well_iterations) {
|
||||
THROW("Conditions not met within " << max_well_iterations<< " iterations.");
|
||||
}
|
||||
}
|
||||
/// \endcode
|
||||
/// \internal[check well conditions]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Transport solver
|
||||
/// \TODO We must call computeTransportSource() here, since we have wells.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp tranport solver
|
||||
/// \internal[tranport solver]
|
||||
transport_solver.solve(&state.faceflux()[0], &porevol[0], &src[0], dt, state.saturation());
|
||||
/// \endcode
|
||||
/// \internal[tranport solver]
|
||||
/// \endinternal
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details Write the output to file.
|
||||
/// \code
|
||||
/// \snippet tutorial4.cpp write output
|
||||
/// \internal[write output]
|
||||
vtkfilename.str("");
|
||||
vtkfilename << "tutorial4-" << std::setw(3) << std::setfill('0') << i << ".vtu";
|
||||
std::ofstream vtkfile(vtkfilename.str().c_str());
|
||||
@@ -360,7 +439,8 @@ int main ()
|
||||
|
||||
destroy_wells(wells);
|
||||
}
|
||||
/// \endcode
|
||||
/// \internal[write output]
|
||||
/// \endinternal
|
||||
|
||||
|
||||
|
||||
@@ -386,4 +466,8 @@ int main ()
|
||||
/// \details
|
||||
/// \section completecode4 Complete source code:
|
||||
/// \include tutorial4.cpp
|
||||
/// \include generate_doc_figures.py
|
||||
|
||||
/// \page tutorial4
|
||||
/// \details
|
||||
/// \section pythonscript4 python script to generate figures:
|
||||
/// \snippet generate_doc_figures.py tutorial4
|
||||
|
||||
Reference in New Issue
Block a user