/// Compute the output. void IncompTpfa::computeResults(TwophaseState& state, WellState& well_state) const { // Make sure h_ contains the direct-solution matrix // and right hand side (not jacobian and residual). // TODO: optimize by only adjusting b and diagonal of A. UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_); ifs_tpfa_assemble(gg, &forces_, &trans_[0], &gpress_omegaweighted_[0], h_); // Make sure h_->x contains the direct solution vector. ASSERT(int(state.pressure().size()) == grid_.number_of_cells); ASSERT(int(state.faceflux().size()) == grid_.number_of_faces); std::copy(state.pressure().begin(), state.pressure().end(), h_->x); std::copy(well_state.bhp().begin(), well_state.bhp().end(), h_->x + grid_.number_of_cells); // Obtain solution. ifs_tpfa_solution soln = { NULL, NULL, NULL, NULL }; soln.cell_press = &state.pressure()[0]; soln.face_flux = &state.faceflux()[0]; if (wells_ != NULL) { ASSERT(int(well_state.bhp().size()) == wells_->number_of_wells); ASSERT(int(well_state.perfRates().size()) == wells_->well_connpos[ wells_->number_of_wells ]); soln.well_flux = &well_state.perfRates()[0]; soln.well_press = &well_state.bhp()[0]; } ifs_tpfa_press_flux(gg, &forces_, &trans_[0], h_, &soln); // TODO: Check what parts of h_ are used here. }
// Solve with no rock compressibility (linear eqn). void IncompTpfa::solveIncomp(const double dt, TwophaseState& state, WellState& well_state) { // Set up properties. computePerSolveDynamicData(dt, state, well_state); // Assemble. UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_); int ok = ifs_tpfa_assemble(gg, &forces_, &trans_[0], &gpress_omegaweighted_[0], h_); if (!ok) { THROW("Failed assembling pressure system."); } // Solve. linsolver_.solve(h_->A, h_->b, h_->x); // Obtain solution. ASSERT(int(state.pressure().size()) == grid_.number_of_cells); ASSERT(int(state.faceflux().size()) == grid_.number_of_faces); ifs_tpfa_solution soln = { NULL, NULL, NULL, NULL }; soln.cell_press = &state.pressure()[0]; soln.face_flux = &state.faceflux()[0]; if (wells_ != NULL) { ASSERT(int(well_state.bhp().size()) == wells_->number_of_wells); ASSERT(int(well_state.perfRates().size()) == wells_->well_connpos[ wells_->number_of_wells ]); soln.well_flux = &well_state.perfRates()[0]; soln.well_press = &well_state.bhp()[0]; } ifs_tpfa_press_flux(gg, &forces_, &trans_[0], h_, &soln); }
void TransportSolverTwophaseReorder::solve(const double* porevolume, const double* source, const double dt, TwophaseState& state) { darcyflux_ = &state.faceflux()[0]; porevolume_ = porevolume; source_ = source; dt_ = dt; toWaterSat(state.saturation(), saturation_); #ifdef EXPERIMENT_GAUSS_SEIDEL std::vector<int> seq(grid_.number_of_cells); std::vector<int> comp(grid_.number_of_cells + 1); int ncomp; compute_sequence_graph(&grid_, darcyflux_, &seq[0], &comp[0], &ncomp, &ia_upw_[0], &ja_upw_[0]); const int nf = grid_.number_of_faces; std::vector<double> neg_darcyflux(nf); std::transform(darcyflux_, darcyflux_ + nf, neg_darcyflux.begin(), std::negate<double>()); compute_sequence_graph(&grid_, &neg_darcyflux[0], &seq[0], &comp[0], &ncomp, &ia_downw_[0], &ja_downw_[0]); #endif std::fill(reorder_iterations_.begin(),reorder_iterations_.end(),0); reorderAndTransport(grid_, darcyflux_); toBothSat(saturation_, state.saturation()); }
// ----------------- 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<EclipseGridParser> deck; boost::scoped_ptr<GridManager> grid; boost::scoped_ptr<IncompPropertiesInterface> props; boost::scoped_ptr<Opm::WellsManager> 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<std::string>("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<double> porevol; computePorevolume(*grid->c_grid(), props->porosity(), porevol); 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 { 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); // 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<double> 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<double> tof; if (use_dg) { bool use_cvi = param.getDefault("use_cvi", false); Opm::TransportModelTracerTofDiscGal tofsolver(*grid->c_grid(), use_cvi); 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<double>(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; }
SimulatorReport SimulatorIncompTwophase::Impl::run(SimulatorTimer& timer, TwophaseState& state, WellState& well_state) { std::vector<double> transport_src; // 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 callback_timer; double time_in_callbacks = 0.0; Opm::time::StopWatch step_timer; Opm::time::StopWatch total_timer; total_timer.start(); double init_satvol[2] = { 0.0 }; double satvol[2] = { 0.0 }; double tot_injected[2] = { 0.0 }; double tot_produced[2] = { 0.0 }; Opm::computeSaturatedVol(porevol, state.saturation(), init_satvol); *log_ << "\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()); } std::fstream tstep_os; if (output_) { std::string filename = output_dir_ + "/step_timing.param"; tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app); } while (!timer.done()) { // Report timestep and (optionally) write state to disk. step_timer.start(); timer.report(*log_); if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { if (output_vtk_) { outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); if (use_reorder_) { // This use of dynamic_cast is not ideal, but should be safe. outputVectorMatlab(std::string("reorder_it"), dynamic_cast<const TransportSolverTwophaseReorder&>(*tsolver_).getReorderIterations(), timer.currentStepNum(), output_dir_); } } SimulatorReport sreport; // Solve pressure equation. if (check_well_controls_) { computeFractionalFlow(props_, allcells_, state.saturation(), 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(); *log_ << "Pressure solver took: " << pt << " seconds." << std::endl; ptime += pt; sreport.pressure_time = pt; // Optionally, check if well controls are satisfied. if (check_well_controls_) { Opm::computePhaseFlowRatesPerWell(*wells_, well_state.perfRates(), fractional_flows, well_resflows_phase); *log_ << "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) { *log_ << "Well controls not passed, solving again." << std::endl; } else { *log_ << "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); // Solve transport. transport_timer.start(); double stepsize = timer.currentStepLength(); if (num_transport_substeps_ != 1) { stepsize /= double(num_transport_substeps_); *log_ << "Making " << num_transport_substeps_ << " transport substeps." << std::endl; } double injected[2] = { 0.0 }; double produced[2] = { 0.0 }; for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) { tsolver_->solve(&initial_porevol[0], &transport_src[0], stepsize, state); double substep_injected[2] = { 0.0 }; double substep_produced[2] = { 0.0 }; Opm::computeInjectedProduced(props_, state.saturation(), transport_src, stepsize, substep_injected, substep_produced); injected[0] += substep_injected[0]; injected[1] += substep_injected[1]; produced[0] += substep_produced[0]; produced[1] += substep_produced[1]; if (use_reorder_ && use_segregation_split_) { // Again, unfortunate but safe use of dynamic_cast. // Possible solution: refactor gravity solver to its own class. dynamic_cast<TransportSolverTwophaseReorder&>(*tsolver_) .solveGravity(&initial_porevol[0], stepsize, state); } 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()); } } transport_timer.stop(); double tt = transport_timer.secsSinceStart(); sreport.transport_time = tt; *log_ << "Transport solver took: " << tt << " seconds." << std::endl; ttime += tt; // Report volume balances. Opm::computeSaturatedVol(porevol, state.saturation(), satvol); tot_injected[0] += injected[0]; tot_injected[1] += injected[1]; tot_produced[0] += produced[0]; tot_produced[1] += produced[1]; reportVolumes(*log_, satvol, tot_porevol_init, tot_injected, tot_produced, injected, produced, init_satvol); sreport.total_time = step_timer.secsSinceStart(); if (output_) { sreport.reportParam(tstep_os); } // advance the timer to the end of the timestep *before* notifying // the client that the timestep is done ++timer; // notify all clients that we are done with the timestep callback_timer.start (); timestep_completed_.signal (); callback_timer.stop (); time_in_callbacks += callback_timer.secsSinceStart (); } if (output_) { if (output_vtk_) { outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); if (use_reorder_) { // This use of dynamic_cast is not ideal, but should be safe. outputVectorMatlab(std::string("reorder_it"), dynamic_cast<const TransportSolverTwophaseReorder&>(*tsolver_).getReorderIterations(), timer.currentStepNum(), output_dir_); } outputWaterCut(watercut, output_dir_); if (wells_) { outputWellReport(wellreport, output_dir_); } tstep_os.close(); } total_timer.stop(); SimulatorReport report; report.pressure_time = ptime; report.transport_time = ttime; report.total_time = total_timer.secsSinceStart() - time_in_callbacks; return report; }