diff --git a/Makefile.am b/Makefile.am
index e7b79c43..70a16dfd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -107,6 +107,8 @@ opm/core/simulator/SimulatorReport.cpp \
opm/core/simulator/SimulatorTimer.cpp \
opm/core/transport/reorder/TransportModelCompressibleTwophase.cpp \
opm/core/transport/reorder/TransportModelInterface.cpp \
+opm/core/transport/reorder/TransportModelTracerTof.cpp \
+opm/core/transport/reorder/TransportModelTracerTofDiscGal.cpp \
opm/core/transport/reorder/TransportModelTwophase.cpp \
opm/core/transport/reorder/nlsolvers.c \
opm/core/transport/reorder/reordersequence.cpp \
@@ -229,6 +231,8 @@ opm/core/transport/SimpleFluid2pWrapper.hpp \
opm/core/transport/SinglePointUpwindTwoPhase.hpp \
opm/core/transport/reorder/TransportModelCompressibleTwophase.hpp \
opm/core/transport/reorder/TransportModelInterface.hpp \
+opm/core/transport/reorder/TransportModelTracerTof.hpp \
+opm/core/transport/reorder/TransportModelTracerTofDiscGal.hpp \
opm/core/transport/reorder/TransportModelTwophase.hpp \
opm/core/transport/reorder/nlsolvers.h \
opm/core/transport/reorder/reordersequence.h \
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 7df4d0b9..f9ded0c3 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -29,6 +29,7 @@ $(BOOST_SYSTEM_LIB)
# Please keep the list sorted.
noinst_PROGRAMS = \
+compute_tof \
refine_wells \
scaneclipsedeck \
sim_2p_comp_reorder \
@@ -43,6 +44,7 @@ wells_example
#
# Please maintain sort order from "noinst_PROGRAMS".
+compute_tof_SOURCES = compute_tof.cpp
refine_wells_SOURCES = refine_wells.cpp
sim_2p_comp_reorder_SOURCES = sim_2p_comp_reorder.cpp
diff --git a/examples/compute_tof.cpp b/examples/compute_tof.cpp
new file mode 100644
index 00000000..046dae38
--- /dev/null
+++ b/examples/compute_tof.cpp
@@ -0,0 +1,254 @@
+/*
+ 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 .
+*/
+
+
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+
+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)
+{
+ using namespace Opm;
+
+ std::cout << "\n================ Test program for incompressible tof computations ===============\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 deck;
+ boost::scoped_ptr grid;
+ boost::scoped_ptr props;
+ boost::scoped_ptr wells;
+ TwophaseState state;
+ // 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("deck_filename");
+ deck.reset(new EclipseGridParser(deck_filename));
+ // Grid init
+ grid.reset(new GridManager(*deck));
+ // Rock and fluid init
+ props.reset(new IncompPropertiesFromDeck(*deck, *grid->c_grid()));
+ // Wells init.
+ wells.reset(new Opm::WellsManager(*deck, *grid->c_grid(), props->permeability()));
+ // Gravity.
+ gravity[2] = deck->hasField("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);
+ }
+ } 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));
+ // Wells init.
+ wells.reset(new Opm::WellsManager());
+ // Gravity.
+ gravity[2] = param.getDefault("gravity", 0.0);
+ // Init state variables (saturation and pressure).
+ initStateBasic(*grid->c_grid(), *props, param, gravity[2], state);
+ }
+
+ // 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
+ std::vector porevol;
+ computePorevolume(*grid->c_grid(), props->porosity(), porevol);
+ int num_cells = grid->c_grid()->number_of_cells;
+ std::vector src(num_cells, 0.0);
+ if (use_deck) {
+ // Do nothing, wells will be the driving force, not source terms.
+ } else {
+ 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("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("pside");
+ double pside_pressure = param.get("pside_pressure");
+ bcs.pressureSide(*grid->c_grid(), FlowBCManager::Side(pside), pside_pressure);
+ }
+
+ // Linear solver.
+ LinearSolverFactory linsolver(param);
+
+ // Pressure solver.
+ Opm::IncompTpfa psolver(*grid->c_grid(), *props, 0, linsolver,
+ 0.0, 0.0, 0,
+ grav, wells->c_wells(), src, bcs.c_bcs());
+
+ // Choice of tof solver.
+ bool use_dg = param.getDefault("use_dg", false);
+ int dg_degree = -1;
+ if (use_dg) {
+ dg_degree = param.getDefault("dg_degree", 0);
+ }
+
+ // Write parameters used for later reference.
+ bool output = param.getDefault("output", true);
+ std::ofstream epoch_os;
+ std::string output_dir;
+ if (output) {
+ output_dir =
+ param.getDefault("output_dir", std::string("output"));
+ boost::filesystem::path fpath(output_dir);
+ try {
+ create_directories(fpath);
+ }
+ catch (...) {
+ THROW("Creating directories failed: " << fpath);
+ }
+ std::string filename = output_dir + "/epoch_timing.param";
+ epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out);
+ // open file to clean it. The file is appended to in SimulatorTwophase
+ filename = output_dir + "/step_timing.param";
+ std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out);
+ step_os.close();
+ param.writeParam(output_dir + "/simulation.param");
+ }
+
+ // Init wells.
+ Opm::WellState well_state;
+ well_state.init(wells->c_wells(), state);
+
+ // Main solvers.
+ 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();
+ std::cout << "\n\n================ Starting main solvers ===============" << std::endl;
+
+ // Solve pressure.
+ pressure_timer.start();
+ psolver.solve(1.0, state, well_state);
+ pressure_timer.stop();
+ double pt = pressure_timer.secsSinceStart();
+ std::cout << "Pressure solver took: " << pt << " seconds." << std::endl;
+ ptime += pt;
+
+ // Process transport sources (to include bdy terms and well flows).
+ std::vector transport_src;
+ Opm::computeTransportSource(*grid->c_grid(), src, state.faceflux(), 1.0,
+ wells->c_wells(), well_state.perfRates(), transport_src);
+
+ // Solve time-of-flight.
+ std::vector tof;
+ if (use_dg) {
+ Opm::TransportModelTracerTofDiscGal tofsolver(*grid->c_grid());
+ transport_timer.start();
+ tofsolver.solveTof(&state.faceflux()[0], &porevol[0], &transport_src[0], dg_degree, tof);
+ transport_timer.stop();
+ } else {
+ Opm::TransportModelTracerTof tofsolver(*grid->c_grid());
+ transport_timer.start();
+ tofsolver.solveTof(&state.faceflux()[0], &porevol[0], &transport_src[0], tof);
+ transport_timer.stop();
+ }
+ double tt = transport_timer.secsSinceStart();
+ std::cout << "Transport solver took: " << tt << " seconds." << std::endl;
+ ttime += tt;
+ total_timer.stop();
+
+ // Output.
+ if (output) {
+ std::string tof_filename = output_dir + "/tof.txt";
+ std::ofstream tof_stream(tof_filename.c_str());
+ std::copy(tof.begin(), tof.end(), std::ostream_iterator(tof_stream, "\n"));
+ }
+
+ std::cout << "\n\n================ End of simulation ===============\n"
+ << "Total time taken: " << total_timer.secsSinceStart()
+ << "\n Pressure time: " << ptime
+ << "\n Transport time: " << ttime << std::endl;
+}
diff --git a/opm/core/transport/reorder/TransportModelInterface.cpp b/opm/core/transport/reorder/TransportModelInterface.cpp
index f8e138a3..8a90ae34 100644
--- a/opm/core/transport/reorder/TransportModelInterface.cpp
+++ b/opm/core/transport/reorder/TransportModelInterface.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include
#include
@@ -31,7 +32,11 @@ void Opm::TransportModelInterface::reorderAndTransport(const UnstructuredGrid& g
std::vector sequence(grid.number_of_cells);
std::vector components(grid.number_of_cells + 1);
int ncomponents;
+ time::StopWatch clock;
+ clock.start();
compute_sequence(&grid, darcyflux, &sequence[0], &components[0], &ncomponents);
+ clock.stop();
+ std::cout << "Topological sort took: " << clock.secsSinceStart() << " seconds." << std::endl;
// Invoke appropriate solve method for each interdependent component.
for (int comp = 0; comp < ncomponents; ++comp) {
diff --git a/opm/core/transport/reorder/TransportModelTracerTof.cpp b/opm/core/transport/reorder/TransportModelTracerTof.cpp
new file mode 100644
index 00000000..8d93f248
--- /dev/null
+++ b/opm/core/transport/reorder/TransportModelTracerTof.cpp
@@ -0,0 +1,122 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Opm
+{
+
+
+ /// Construct solver.
+ /// \param[in] grid A 2d or 3d grid.
+ TransportModelTracerTof::TransportModelTracerTof(const UnstructuredGrid& grid)
+ : grid_(grid)
+ {
+ }
+
+
+
+
+ /// Solve for time-of-flight.
+ /// \param[in] darcyflux Array of signed face fluxes.
+ /// \param[in] porevolume Array of pore volumes.
+ /// \param[in] source Source term. Sign convention is:
+ /// (+) inflow flux,
+ /// (-) outflow flux.
+ /// \param[out] tof Array of time-of-flight values.
+ void TransportModelTracerTof::solveTof(const double* darcyflux,
+ const double* porevolume,
+ const double* source,
+ std::vector& tof)
+ {
+ darcyflux_ = darcyflux;
+ porevolume_ = porevolume;
+ source_ = source;
+#ifndef NDEBUG
+ // Sanity check for sources.
+ const double cum_src = std::accumulate(source, source + grid_.number_of_cells, 0.0);
+ if (std::fabs(cum_src) > *std::max_element(source, source + grid_.number_of_cells)*1e-2) {
+ THROW("Sources do not sum to zero: " << cum_src);
+ }
+#endif
+ tof.resize(grid_.number_of_cells);
+ std::fill(tof.begin(), tof.end(), 0.0);
+ tof_ = &tof[0];
+ reorderAndTransport(grid_, darcyflux);
+ }
+
+
+
+
+ void TransportModelTracerTof::solveSingleCell(const int cell)
+ {
+ // Compute flux terms.
+ // Sources have zero tof, and therefore do not contribute
+ // to upwind_term. Sinks on the other hand, must be added
+ // to the downwind_flux (note sign change resulting from
+ // different sign conventions: pos. source is injection,
+ // pos. flux is outflow).
+ double upwind_term = 0.0;
+ double downwind_flux = std::max(-source_[cell], 0.0);
+ for (int i = grid_.cell_facepos[cell]; i < grid_.cell_facepos[cell+1]; ++i) {
+ int f = grid_.cell_faces[i];
+ double flux;
+ int other;
+ // Compute cell flux
+ if (cell == grid_.face_cells[2*f]) {
+ flux = darcyflux_[f];
+ other = grid_.face_cells[2*f+1];
+ } else {
+ flux =-darcyflux_[f];
+ other = grid_.face_cells[2*f];
+ }
+ // Add flux to upwind_term or downwind_flux, if interior.
+ if (other != -1) {
+ if (flux < 0.0) {
+ upwind_term += flux*tof_[other];
+ } else {
+ downwind_flux += flux;
+ }
+ }
+ }
+
+ // Compute tof.
+ tof_[cell] = (porevolume_[cell] - upwind_term)/downwind_flux;
+ }
+
+
+
+
+ void TransportModelTracerTof::solveMultiCell(const int num_cells, const int* cells)
+ {
+ std::cout << "Pretending to solve multi-cell dependent equation with " << num_cells << " cells." << std::endl;
+ for (int i = 0; i < num_cells; ++i) {
+ solveSingleCell(cells[i]);
+ }
+ }
+
+
+
+
+} // namespace Opm
diff --git a/opm/core/transport/reorder/TransportModelTracerTof.hpp b/opm/core/transport/reorder/TransportModelTracerTof.hpp
new file mode 100644
index 00000000..270ac328
--- /dev/null
+++ b/opm/core/transport/reorder/TransportModelTracerTof.hpp
@@ -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 .
+*/
+
+#ifndef OPM_TRANSPORTMODELTRACERTOF_HEADER_INCLUDED
+#define OPM_TRANSPORTMODELTRACERTOF_HEADER_INCLUDED
+
+#include
+#include
+#include