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(*BaseType::eclipse_state_, *wells, Opm::UgGridHelpers::numCells(BaseType::grid_), timer.currentStepNum())); } else { OPM_MESSAGE("Warning: simulating with no WPOLYMER in deck (no polymer will be injected)."); 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; if (has_plyshlog_) { computeRepRadiusPerfLength(*BaseType::eclipse_state_, timer.currentStepNum(), BaseType::grid_, wells_rep_radius_, wells_perf_length_, wells_bore_diameter_); } }
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.getCellData( 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.getCellData( state.CONCENTRATION ), state.getCellData( state.CMAX ) , 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.getCellData( state.CONCENTRATION ), state.getCellData( state.CMAX )); 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.getCellData( state.CONCENTRATION ), state.getCellData( state.CMAX )); } } 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.getCellData( 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; }
// ----------------- 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; std::unique_ptr<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::ParseContext parseContext({{ ParseContext::PARSE_RANDOM_SLASH , InputError::IGNORE }}); deck = parser->parseFile(deck_filename , parseContext); eclipseState.reset(new Opm::EclipseState(deck , parseContext)); // Grid init grid.reset(new GridManager(deck)); { const UnstructuredGrid& ug_grid = *(grid->c_grid()); // Rock and fluid init props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, ug_grid)); // check_well_controls = param.getDefault("check_well_controls", false); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); state.reset( new PolymerBlackoilState( UgGridHelpers::numCells( ug_grid ) , UgGridHelpers::numFaces( ug_grid ), 2)); // 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(ug_grid, *props, param, gravity[2], *state); } else { initStateFromDeck(ug_grid, *props, deck, gravity[2], *state); } initBlackoilSurfvol(ug_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)); { const UnstructuredGrid& ug_grid = *(grid->c_grid()); // Rock and fluid init. props.reset(new BlackoilPropertiesBasic(param, ug_grid.dimensions, UgGridHelpers::numCells( ug_grid ))); state.reset( new PolymerBlackoilState( UgGridHelpers::numCells( ug_grid ) , UgGridHelpers::numFaces( ug_grid ) , 2)); // Rock compressibility. rock_comp.reset(new RockCompressibility(param)); // Gravity. gravity[2] = param.getDefault("gravity", 0.0); // Init state variables (saturation and pressure). initStateBasic(ug_grid, *props, param, gravity[2], *state); initBlackoilSurfvol(ug_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 < UgGridHelpers::numCells( ug_grid ); ++cell) { double smin[2], smax[2]; auto& saturation = state->saturation(); auto& concentration = state->getCellData( state->CONCENTRATION ); auto& max_concentration = state->getCellData( state->CMAX ); props->satRange(1, &cell, smin, smax); if (saturation[2*cell] > 0.5*(smin[0] + smax[0])) { concentration[cell] = poly_init; max_concentration[cell] = poly_init; } else { saturation[2*cell + 0] = 0.; saturation[2*cell + 1] = 1.; concentration[cell] = 0.; max_concentration[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(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; }
SimulatorReport SimulatorCompressibleAd::Impl::run(SimulatorTimer& timer, BlackoilState& 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 step_timer; Opm::time::StopWatch total_timer; total_timer.start(); double init_surfvol[2] = { 0.0 }; double inplace_surfvol[2] = { 0.0 }; double tot_injected[2] = { 0.0 }; double tot_produced[2] = { 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()); } 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); } for (; !timer.done(); ++timer) { // Report timestep and (optionally) write state to disk. step_timer.start(); 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_); } SimulatorReport sreport; // Solve pressure equation. if (check_well_controls_) { computeFractionalFlow(props_, allcells_, state.pressure(), state.surfacevol(), 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); #if 0 // 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; } } #endif // Stop timer and report. pressure_timer.stop(); double pt = pressure_timer.secsSinceStart(); std::cout << "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); 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_) { THROW("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 from well flows. Opm::computeTransportSource(props_, wells_, well_state, transport_src); // Solve transport. transport_timer.start(); double stepsize = timer.currentStepLength(); 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 }; for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) { tsolver_.solve(&state.faceflux()[0], &state.pressure()[0], &initial_porevol[0], &porevol[0], &transport_src[0], stepsize, state.saturation(), state.surfacevol()); double substep_injected[2] = { 0.0 }; double substep_produced[2] = { 0.0 }; Opm::computeInjectedProduced(props_, state, 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 (gravity_ != 0 && use_segregation_split_) { tsolver_.solveGravity(columns_, stepsize, state.saturation(), state.surfacevol()); } } transport_timer.stop(); double tt = transport_timer.secsSinceStart(); sreport.transport_time = tt; std::cout << "Transport solver took: " << tt << " seconds." << std::endl; ttime += tt; // Report volume balances. Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol); tot_injected[0] += injected[0]; tot_injected[1] += injected[1]; tot_produced[0] += produced[0]; tot_produced[1] += produced[1]; std::cout.precision(5); const int width = 18; std::cout << "\nMass balance report.\n"; std::cout << " Injected surface volumes: " << std::setw(width) << injected[0] << std::setw(width) << injected[1] << std::endl; std::cout << " Produced surface volumes: " << std::setw(width) << produced[0] << std::setw(width) << produced[1] << std::endl; std::cout << " Total inj surface volumes: " << std::setw(width) << tot_injected[0] << std::setw(width) << tot_injected[1] << std::endl; std::cout << " Total prod surface volumes: " << std::setw(width) << tot_produced[0] << std::setw(width) << tot_produced[1] << std::endl; const double balance[2] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0], init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1] }; std::cout << " Initial - inplace + inj - prod: " << std::setw(width) << balance[0] << std::setw(width) << balance[1] << 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::endl; std::cout.precision(8); watercut.push(timer.currentTime() + 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.currentTime() + timer.currentStepLength(), well_state.bhp(), well_state.perfRates()); } sreport.total_time = step_timer.secsSinceStart(); if (output_) { sreport.reportParam(tstep_os); } } 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_); } tstep_os.close(); } total_timer.stop(); SimulatorReport report; report.pressure_time = ptime; report.transport_time = ttime; report.total_time = total_timer.secsSinceStart(); return report; }
// ----------------- Main program ----------------- int main(int argc, char** argv) { using namespace Opm; std::cout << "\n================ Test program for incompressible two-phase 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"); boost::scoped_ptr<EclipseGridParser> deck; boost::scoped_ptr<GridManager> grid; boost::scoped_ptr<IncompPropertiesInterface> props; boost::scoped_ptr<RockCompressibility> rock_comp; 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())); // 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)); // 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)); // 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); } // 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); 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"); } std::cout << "\n\n================ Starting main simulation loop ===============\n" << " (number of epochs: " << (use_deck ? deck->numberOfEpochs() : 1) << ")\n\n" << std::flush; SimulatorReport rep; if (!use_deck) { // Simple simulation without a deck. WellsManager wells; // no wells. SimulatorIncompTwophase simulator(param, *grid->c_grid(), *props, rock_comp->isActive() ? rock_comp.get() : 0, wells, 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; SimulatorTimer simtimer; // Use timer for last epoch to obtain total time. deck->setCurrentEpoch(deck->numberOfEpochs() - 1); simtimer.init(*deck); const double total_time = simtimer.totalTime(); for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { // Set epoch index. deck->setCurrentEpoch(epoch); // Update the timer. if (deck->hasField("TSTEP")) { simtimer.init(*deck); } else { if (epoch != 0) { THROW("No TSTEP in deck for epoch " << epoch); } simtimer.init(param); } simtimer.setCurrentStepNum(step); simtimer.setTotalTime(total_time); // Report on start of epoch. std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" << "\n (number of steps: " << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, well_state WellsManager wells(*deck, *grid->c_grid(), props->permeability()); // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every epoch, // since number of wells may change etc. if (epoch == 0) { well_state.init(wells.c_wells(), state); } // Create and run simulator. SimulatorIncompTwophase simulator(param, *grid->c_grid(), *props, rock_comp->isActive() ? rock_comp.get() : 0, wells, src, bcs.c_bcs(), linsolver, grav); if (epoch == 0) { warnIfUnusedParams(param); } SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); if (output) { epoch_rep.reportParam(epoch_os); } // 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); if (output) { std::string filename = output_dir + "/walltime.param"; std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); rep.reportParam(tot_os); } }
// ----------------- 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<EclipseGridParser> deck; boost::scoped_ptr<GridManager> grid; boost::scoped_ptr<BlackoilPropertiesInterface> props; boost::scoped_ptr<RockCompressibility> rock_comp; 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()); eclipseState.reset(new Opm::EclipseState(parser->parseFile(deck_filename))); deck.reset(new EclipseGridParser(deck_filename)); // Grid init grid.reset(new GridManager(*deck)); // Rock and fluid init props.reset(new BlackoilPropertiesFromDeck(*deck, *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)); // 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); } initBlackoilSurfvol(*grid->c_grid(), *props, state); // Init polymer properties. poly_props.readFromDeck(*deck); } 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; 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); } bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); 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" << " (number of epochs: " << (use_deck ? deck->numberOfEpochs() : 1) << ")\n\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, 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; SimulatorTimer simtimer; // Use timer for last epoch to obtain total time. deck->setCurrentEpoch(deck->numberOfEpochs() - 1); simtimer.init(*deck); const double total_time = simtimer.totalTime(); // Check for WPOLYMER presence in last epoch to decide // polymer injection control type. const bool use_wpolymer = deck->hasField("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 (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { // Set epoch index. deck->setCurrentEpoch(epoch); // Update the timer. if (deck->hasField("TSTEP")) { simtimer.init(*deck); } else { if (epoch != 0) { OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); } simtimer.init(param); } simtimer.setCurrentStepNum(step); simtimer.setTotalTime(total_time); // Report on start of epoch. std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" << "\n (number of steps: " << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, polymer inflow controls. WellsManager wells(eclipseState , epoch , *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, *wells.c_wells(), props->numCells())); } 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 epoch, // since number of wells may change etc. if (epoch == 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, src, bcs.c_bcs(), linsolver, grav); if (epoch == 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; }
SimulatorReport SimulatorPolymer::Impl::run(SimulatorTimer& timer, PolymerState& 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); // 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 init_polymass = 0.0; double satvol[2] = { 0.0 }; double polymass = 0.0; double polymass_adsorbed = 0.0; 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()); } for (; !timer.done(); ++timer) { // Report timestep and (optionally) write state to disk. timer.report(std::cout); if (output_ && (timer.currentStepNum() % output_interval_ == 0)) { outputState(grid_, state, timer.currentStepNum(), output_dir_); } // Solve pressure. do { pressure_timer.start(); psolver_.solve(timer.currentStepLength(), state, well_state); pressure_timer.stop(); double pt = pressure_timer.secsSinceStart(); std::cout << "Pressure solver took: " << pt << " seconds." << std::endl; ptime += pt; } while (false); // Update pore volumes if rock is compressible. if (rock_comp_props_ && rock_comp_props_->isActive()) { 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.currentTime(); double stepsize = timer.currentStepLength(); const double inflowc0 = poly_inflow_(current_time + 1e-5*stepsize); const double inflowc1 = poly_inflow_(current_time + (1.0 - 1e-5)*stepsize); if (inflowc0 != inflowc1) { std::cout << "**** Warning: polymer inflow rate changes during timestep. Using rate near start of step."; } const double inflow_c = inflowc0; // 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; } for (int tr_substep = 0; tr_substep < num_transport_substeps_; ++tr_substep) { tsolver_.solve(&state.faceflux()[0], &porevol[0], &transport_src[0], stepsize, inflow_c, state.saturation(), state.concentration(), state.maxconcentration()); Opm::computeInjectedProduced(props_, poly_props_, state.saturation(), state.concentration(), state.maxconcentration(), transport_src, timer.currentStepLength(), inflow_c, injected, produced, polyinj, 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.currentTime() + timer.currentStepLength(), produced[0]/(produced[0] + produced[1]), tot_produced[0]/tot_porevol_init); if (wells_) { wellreport.push(props_, *wells_, state.saturation(), timer.currentTime() + timer.currentStepLength(), well_state.bhp(), well_state.perfRates()); } } if (output_) { outputState(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; }
// ----------------- Main program ----------------- int main(int argc, char** argv) try { using namespace Opm; std::cout << "\n================ Test program for incompressible two-phase flow ===============\n\n"; parameter::ParameterGroup param(argc, argv, false); std::cout << "--------------- Reading parameters ---------------" << std::endl; #if ! HAVE_SUITESPARSE_UMFPACK_H // This is an extra check to intercept a potentially invalid request for the // implicit transport solver as early as possible for the user. { const bool use_reorder = param.getDefault("use_reorder", true); if (!use_reorder) { OPM_THROW(std::runtime_error, "Cannot use implicit transport solver without UMFPACK. " "Either reconfigure opm-core with SuiteSparse/UMFPACK support and recompile, " "or use the reordering solver (use_reorder=true)."); } } #endif // If we have a "deck_filename", grid and props will be read from that. bool use_deck = param.has("deck_filename"); EclipseStateConstPtr eclipseState; Opm::DeckConstPtr deck; std::unique_ptr<GridManager> grid; std::unique_ptr<IncompPropertiesInterface> props; std::unique_ptr<RockCompressibility> rock_comp; std::unique_ptr<TwophaseState> state; // bool check_well_controls = false; // int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; if (use_deck) { ParserPtr parser(new Opm::Parser()); ParseContext parseContext; std::string deck_filename = param.get<std::string>("deck_filename"); deck = parser->parseFile(deck_filename , parseContext); eclipseState.reset( new EclipseState(*deck, parseContext)); // Grid init grid.reset(new GridManager(*eclipseState->getInputGrid())); { const UnstructuredGrid& ug_grid = *(grid->c_grid()); // Rock and fluid init props.reset(new IncompPropertiesFromDeck(deck, eclipseState, ug_grid)); state.reset( new TwophaseState( UgGridHelpers::numCells( ug_grid ) , UgGridHelpers::numFaces( ug_grid ))); // 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(ug_grid, *props, param, gravity[2], *state); } else { initStateFromDeck(ug_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)); { const UnstructuredGrid& ug_grid = *(grid->c_grid()); // Rock and fluid init. props.reset(new IncompPropertiesBasic(param, ug_grid.dimensions, UgGridHelpers::numCells( ug_grid ))); state.reset( new TwophaseState( UgGridHelpers::numCells( ug_grid ) , UgGridHelpers::numFaces( ug_grid ))); // Rock compressibility. rock_comp.reset(new RockCompressibility(param)); // Gravity. gravity[2] = param.getDefault("gravity", 0.0); // Init state variables (saturation and pressure). initStateBasic(ug_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 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); 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 (...) { OPM_THROW(std::runtime_error, "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"); } SimulatorReport rep; if (!use_deck) { std::cout << "\n\n================ Starting main simulation loop ===============\n" << " (number of report steps: 1)\n\n" << std::flush; // Simple simulation without a deck. WellsManager wells; // no wells. SimulatorIncompTwophase simulator(param, *grid->c_grid(), *props, rock_comp->isActive() ? rock_comp.get() : 0, wells, 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. Opm::TimeMapConstPtr timeMap = eclipseState->getSchedule()->getTimeMap(); std::cout << "\n\n================ Starting main simulation loop ===============\n" << " (number of report steps: " << timeMap->numTimesteps() << ")\n\n" << std::flush; WellState well_state; int step = 0; SimulatorTimer simtimer; // Use timer for last epoch to obtain total time. simtimer.init(timeMap); const double total_time = simtimer.totalTime(); // for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) { size_t reportStepIdx = 0; // Only handle a single, unchanging well setup. { // Update the timer. simtimer.setCurrentStepNum(step); simtimer.setTotalTime(total_time); // Report on start of report step. // std::cout << "\n\n-------------- Starting report step " << reportStepIdx << " --------------" // << "\n (number of time steps: " // << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, well_state WellsManager wells(eclipseState , reportStepIdx , *grid->c_grid(), props->permeability()); // @@@ 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. SimulatorIncompTwophase simulator(param, *grid->c_grid(), *props, rock_comp->isActive() ? rock_comp.get() : 0, wells, src, bcs.c_bcs(), linsolver, grav); if (reportStepIdx == 0) { warnIfUnusedParams(param); } SimulatorReport epoch_rep = simulator.run(simtimer, *state, well_state); if (output) { epoch_rep.reportParam(epoch_os); } // 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); if (output) { std::string filename = output_dir + "/walltime.param"; std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); rep.reportParam(tot_os); } } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; throw; }
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; }
SimulatorReport SimulatorFullyImplicitCompressiblePolymer::Impl::run(SimulatorTimer& timer, PolymerBlackoilState& state) { WellStateFullyImplicitBlackoil prev_well_state; // 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); } std::vector<double> initial_porevol = porevol; std::vector<double> polymer_inflow_c(grid_.number_of_cells); // Main simulation loop. Opm::time::StopWatch solver_timer; double stime = 0.0; Opm::time::StopWatch step_timer; Opm::time::StopWatch total_timer; total_timer.start(); std::string tstep_filename = output_dir_ + "/step_timing.txt"; std::ofstream tstep_os(tstep_filename.c_str()); //Main simulation loop. while (!timer.done()) { #if 0 double tot_injected[2] = { 0.0 }; double tot_produced[2] = { 0.0 }; Opm::Watercut watercut; watercut.push(0.0, 0.0, 0.0); 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); } 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); } #endif // Report timestep and (optionally) write state to disk. step_timer.start(); timer.report(std::cout); WellsManager wells_manager(eclipse_state_, timer.currentStepNum(), Opm::UgGridHelpers::numCells(grid_), Opm::UgGridHelpers::globalCell(grid_), Opm::UgGridHelpers::cartDims(grid_), Opm::UgGridHelpers::dimensions(grid_), Opm::UgGridHelpers::cell2Faces(grid_), Opm::UgGridHelpers::beginFaceCentroids(grid_), props_.permeability()); const Wells* wells = wells_manager.c_wells(); WellStateFullyImplicitBlackoil well_state; well_state.init(wells, state.blackoilState(), prev_well_state); //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_, eclipse_state_, *wells, Opm::UgGridHelpers::numCells(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(grid_)); polymer_inflow_ptr->getInflowValues(timer.simulationTimeElapsed(), timer.simulationTimeElapsed() + timer.currentStepLength(), polymer_inflow_c); 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 (output_) { if (timer.currentStepNum() == 0) { output_writer_.writeInit(timer); } output_writer_.writeTimeStep(timer, state.blackoilState(), well_state); } // Run solver. solver_timer.start(); FullyImplicitCompressiblePolymerSolver solver(grid_, props_, geo_, rock_comp_props_, polymer_props_, *wells_manager.c_wells(), linsolver_); solver.step(timer.currentStepLength(), state, well_state, polymer_inflow_c); // Stop timer and report. solver_timer.stop(); const double st = solver_timer.secsSinceStart(); std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; stime += st; // 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); } /* double injected[2] = { 0.0 }; double produced[2] = { 0.0 }; double polyinj = 0; double polyprod = 0; Opm::computeInjectedProduced(props_, polymer_props_, state, transport_src, polymer_inflow_c, timer.currentStepLength(), injected, produced, polyinj, polyprod); tot_injected[0] += injected[0]; tot_injected[1] += injected[1]; tot_produced[0] += produced[0]; tot_produced[1] += produced[1]; watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), produced[0]/(produced[0] + produced[1]), tot_produced[0]/tot_porevol_init); std::cout.precision(5); const int width = 18; std::cout << "\nMass balance report.\n"; std::cout << " Injected reservoir volumes: " << std::setw(width) << injected[0] << std::setw(width) << injected[1] << std::endl; std::cout << " Produced reservoir volumes: " << std::setw(width) << produced[0] << std::setw(width) << produced[1] << std::endl; std::cout << " Total inj reservoir volumes: " << std::setw(width) << tot_injected[0] << std::setw(width) << tot_injected[1] << std::endl; std::cout << " Total prod reservoir volumes: " << std::setw(width) << tot_produced[0] << std::setw(width) << tot_produced[1] << std::endl; */ if (output_) { SimulatorReport step_report; step_report.pressure_time = st; step_report.total_time = step_timer.secsSinceStart(); step_report.reportParam(tstep_os); } ++timer; prev_well_state = well_state; } // Write final simulation state. if (output_) { if (output_vtk_) { outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); output_writer_.writeTimeStep(timer, state.blackoilState(), prev_well_state); } total_timer.stop(); SimulatorReport report; report.pressure_time = stime; report.transport_time = 0.0; report.total_time = total_timer.secsSinceStart(); return report; }
SimulatorReport SimulatorFullyImplicitBlackoil::Impl::run(SimulatorTimer& timer, BlackoilState& state, WellState& well_state) { // 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 solver_timer; double stime = 0.0; Opm::time::StopWatch step_timer; Opm::time::StopWatch total_timer; total_timer.start(); #if 0 // These must be changed for three-phase. double init_surfvol[2] = { 0.0 }; double inplace_surfvol[2] = { 0.0 }; double tot_injected[2] = { 0.0 }; double tot_produced[2] = { 0.0 }; Opm::computeSaturatedVol(porevol, state.surfacevol(), init_surfvol); Opm::Watercut watercut; watercut.push(0.0, 0.0, 0.0); Opm::WellReport wellreport; #endif 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); #if 0 wellreport.push(props_, *wells_, state.pressure(), state.surfacevol(), state.saturation(), 0.0, well_state.bhp(), well_state.perfRates()); #endif } 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); } for (; !timer.done(); ++timer) { // Report timestep and (optionally) write state to disk. step_timer.start(); 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_); outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); } SimulatorReport sreport; // Solve pressure equation. // if (check_well_controls_) { // computeFractionalFlow(props_, allcells_, // state.pressure(), state.surfacevol(), 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. solver_timer.start(); std::vector<double> initial_pressure = state.pressure(); solver_.step(timer.currentStepLength(), state, well_state); // Stop timer and report. solver_timer.stop(); const double st = solver_timer.secsSinceStart(); std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; stime += st; sreport.pressure_time = st; // 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); } // The reports below are geared towards two phases only. #if 0 // Report mass balances. double injected[2] = { 0.0 }; double produced[2] = { 0.0 }; Opm::computeInjectedProduced(props_, state, transport_src, stepsize, injected, produced); Opm::computeSaturatedVol(porevol, state.surfacevol(), inplace_surfvol); tot_injected[0] += injected[0]; tot_injected[1] += injected[1]; tot_produced[0] += produced[0]; tot_produced[1] += produced[1]; std::cout.precision(5); const int width = 18; std::cout << "\nMass balance report.\n"; std::cout << " Injected surface volumes: " << std::setw(width) << injected[0] << std::setw(width) << injected[1] << std::endl; std::cout << " Produced surface volumes: " << std::setw(width) << produced[0] << std::setw(width) << produced[1] << std::endl; std::cout << " Total inj surface volumes: " << std::setw(width) << tot_injected[0] << std::setw(width) << tot_injected[1] << std::endl; std::cout << " Total prod surface volumes: " << std::setw(width) << tot_produced[0] << std::setw(width) << tot_produced[1] << std::endl; const double balance[2] = { init_surfvol[0] - inplace_surfvol[0] - tot_produced[0] + tot_injected[0], init_surfvol[1] - inplace_surfvol[1] - tot_produced[1] + tot_injected[1] }; std::cout << " Initial - inplace + inj - prod: " << std::setw(width) << balance[0] << std::setw(width) << balance[1] << 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::endl; std::cout.precision(8); // Make well reports. watercut.push(timer.currentTime() + 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.currentTime() + timer.currentStepLength(), well_state.bhp(), well_state.perfRates()); } #endif sreport.total_time = step_timer.secsSinceStart(); if (output_) { sreport.reportParam(tstep_os); } } if (output_) { if (output_vtk_) { outputStateVtk(grid_, state, timer.currentStepNum(), output_dir_); } outputStateMatlab(grid_, state, timer.currentStepNum(), output_dir_); outputWellStateMatlab(well_state,timer.currentStepNum(), output_dir_); #if 0 outputWaterCut(watercut, output_dir_); if (wells_) { outputWellReport(wellreport, output_dir_); } #endif tstep_os.close(); } total_timer.stop(); SimulatorReport report; report.pressure_time = stime; report.transport_time = 0.0; report.total_time = total_timer.secsSinceStart(); return report; }
SimulatorReport SimulatorBase<Implementation>::run(SimulatorTimer& timer, ReservoirState& state) { WellState prev_well_state; // Create timers and file for writing timing info. Opm::time::StopWatch solver_timer; double stime = 0.0; Opm::time::StopWatch step_timer; Opm::time::StopWatch total_timer; total_timer.start(); std::string tstep_filename = output_writer_.outputDirectory() + "/step_timing.txt"; std::ofstream tstep_os(tstep_filename.c_str()); // adaptive time stepping std::unique_ptr< AdaptiveTimeStepping > adaptiveTimeStepping; if( param_.getDefault("timestep.adaptive", true ) ) { adaptiveTimeStepping.reset( new AdaptiveTimeStepping( param_, solver_.parallelInformation() ) ); } // init output writer output_writer_.writeInit( timer ); std::string restorefilename = param_.getDefault("restorefile", std::string("") ); if( ! restorefilename.empty() ) { // -1 means that we'll take the last report step that was written const int desiredRestoreStep = param_.getDefault("restorestep", int(-1) ); output_writer_.restore( timer, state, prev_well_state, restorefilename, desiredRestoreStep ); } unsigned int totalNewtonIterations = 0; unsigned int totalLinearIterations = 0; // Main simulation loop. while (!timer.done()) { // Report timestep. step_timer.start(); if ( terminal_output_ ) { timer.report(std::cout); } // Create wells and well state. WellsManager wells_manager(eclipse_state_, timer.currentStepNum(), Opm::UgGridHelpers::numCells(grid_), Opm::UgGridHelpers::globalCell(grid_), Opm::UgGridHelpers::cartDims(grid_), Opm::UgGridHelpers::dimensions(grid_), Opm::UgGridHelpers::cell2Faces(grid_), Opm::UgGridHelpers::beginFaceCentroids(grid_), props_.permeability(), is_parallel_run_); const Wells* wells = wells_manager.c_wells(); WellState well_state; well_state.init(wells, state, prev_well_state); // give the polymer and surfactant simulators the chance to do their stuff asImpl().handleAdditionalWellInflow(timer, wells_manager, well_state, wells); // write simulation state at the report stage output_writer_.writeTimeStep( timer, state, well_state ); // Max oil saturation (for VPPARS), hysteresis update. props_.updateSatOilMax(state.saturation()); props_.updateSatHyst(state.saturation(), allcells_); // Compute reservoir volumes for RESV controls. asImpl().computeRESV(timer.currentStepNum(), wells, state, well_state); // Run a multiple steps of the solver depending on the time step control. solver_timer.start(); auto solver = asImpl().createSolver(wells); // If sub stepping is enabled allow the solver to sub cycle // in case the report steps are too large for the solver to converge // // \Note: The report steps are met in any case // \Note: The sub stepping will require a copy of the state variables if( adaptiveTimeStepping ) { adaptiveTimeStepping->step( timer, *solver, state, well_state, output_writer_ ); } else { // solve for complete report step solver->step(timer.currentStepLength(), state, well_state); } // take time that was used to solve system for this reportStep solver_timer.stop(); // accumulate the number of Newton and Linear Iterations totalNewtonIterations += solver->newtonIterations(); totalLinearIterations += solver->linearIterations(); // Report timing. const double st = solver_timer.secsSinceStart(); if ( terminal_output_ ) { std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl; } stime += st; if ( output_writer_.output() ) { SimulatorReport step_report; step_report.pressure_time = st; step_report.total_time = step_timer.secsSinceStart(); step_report.reportParam(tstep_os); } // Increment timer, remember well state. ++timer; prev_well_state = well_state; } // Write final simulation state. output_writer_.writeTimeStep( timer, state, prev_well_state ); // Stop timer and create timing report total_timer.stop(); SimulatorReport report; report.pressure_time = stime; report.transport_time = 0.0; report.total_time = total_timer.secsSinceStart(); report.total_newton_iterations = totalNewtonIterations; report.total_linear_iterations = totalLinearIterations; return report; }
SimulatorReport SimulatorBase<Implementation>::run(SimulatorTimer& timer, ReservoirState& state) { WellState prev_well_state; if (output_writer_.isRestart()) { // This is a restart, populate WellState and ReservoirState state objects from restart file output_writer_.initFromRestartFile(props_.phaseUsage(), props_.permeability(), grid_, state, prev_well_state); } // Create timers and file for writing timing info. Opm::time::StopWatch solver_timer; double stime = 0.0; Opm::time::StopWatch step_timer; Opm::time::StopWatch total_timer; total_timer.start(); std::string tstep_filename = output_writer_.outputDirectory() + "/step_timing.txt"; std::ofstream tstep_os(tstep_filename.c_str()); const auto& schedule = eclipse_state_->getSchedule(); const auto& events = schedule->getEvents(); // adaptive time stepping std::unique_ptr< AdaptiveTimeStepping > adaptiveTimeStepping; if( param_.getDefault("timestep.adaptive", true ) ) { adaptiveTimeStepping.reset( new AdaptiveTimeStepping( param_, terminal_output_ ) ); } // init output writer output_writer_.writeInit( timer ); std::string restorefilename = param_.getDefault("restorefile", std::string("") ); if( ! restorefilename.empty() ) { // -1 means that we'll take the last report step that was written const int desiredRestoreStep = param_.getDefault("restorestep", int(-1) ); output_writer_.restore( timer, state, prev_well_state, restorefilename, desiredRestoreStep ); } unsigned int totalNonlinearIterations = 0; unsigned int totalLinearIterations = 0; bool is_well_potentials_computed = param_.getDefault("compute_well_potentials", false ); std::vector<double> well_potentials; // Main simulation loop. while (!timer.done()) { // Report timestep. step_timer.start(); if ( terminal_output_ ) { timer.report(std::cout); } // Create wells and well state. WellsManager wells_manager(eclipse_state_, timer.currentStepNum(), Opm::UgGridHelpers::numCells(grid_), Opm::UgGridHelpers::globalCell(grid_), Opm::UgGridHelpers::cartDims(grid_), Opm::UgGridHelpers::dimensions(grid_), Opm::UgGridHelpers::cell2Faces(grid_), Opm::UgGridHelpers::beginFaceCentroids(grid_), props_.permeability(), is_parallel_run_, well_potentials); const Wells* wells = wells_manager.c_wells(); WellState well_state; well_state.init(wells, state, prev_well_state); // give the polymer and surfactant simulators the chance to do their stuff asImpl().handleAdditionalWellInflow(timer, wells_manager, well_state, wells); // write simulation state at the report stage output_writer_.writeTimeStep( timer, state, well_state ); // Max oil saturation (for VPPARS), hysteresis update. props_.updateSatOilMax(state.saturation()); props_.updateSatHyst(state.saturation(), allcells_); // Compute reservoir volumes for RESV controls. asImpl().computeRESV(timer.currentStepNum(), wells, state, well_state); // Run a multiple steps of the solver depending on the time step control. solver_timer.start(); auto solver = asImpl().createSolver(wells); // If sub stepping is enabled allow the solver to sub cycle // in case the report steps are too large for the solver to converge // // \Note: The report steps are met in any case // \Note: The sub stepping will require a copy of the state variables if( adaptiveTimeStepping ) { adaptiveTimeStepping->step( timer, *solver, state, well_state, output_writer_ ); } else { // solve for complete report step solver->step(timer.currentStepLength(), state, well_state); } // update the derived geology (transmissibilities, pore volumes, etc) if the // has geology changed for the next report step const int nextTimeStepIdx = timer.currentStepNum() + 1; if (nextTimeStepIdx < timer.numSteps() && events.hasEvent(ScheduleEvents::GEO_MODIFIER, nextTimeStepIdx)) { // bring the contents of the keywords to the current state of the SCHEDULE // section // // TODO (?): handle the parallel case (maybe this works out of the box) DeckConstPtr miniDeck = schedule->getModifierDeck(nextTimeStepIdx); eclipse_state_->applyModifierDeck(miniDeck); geo_.update(grid_, props_, eclipse_state_, gravity_); } // take time that was used to solve system for this reportStep solver_timer.stop(); // accumulate the number of nonlinear and linear Iterations totalNonlinearIterations += solver->nonlinearIterations(); totalLinearIterations += solver->linearIterations(); // Report timing. const double st = solver_timer.secsSinceStart(); // accumulate total time stime += st; if ( terminal_output_ ) { std::cout << "Fully implicit solver took: " << st << " seconds. Total solver time taken: " << stime << " seconds." << std::endl; } if ( output_writer_.output() ) { SimulatorReport step_report; step_report.pressure_time = st; step_report.total_time = step_timer.secsSinceStart(); step_report.reportParam(tstep_os); } // Increment timer, remember well state. ++timer; prev_well_state = well_state; // The well potentials are only computed if they are needed // For now thay are only used to determine default guide rates for group controlled wells if ( is_well_potentials_computed ) { asImpl().computeWellPotentials(wells, well_state, well_potentials); } } // Write final simulation state. output_writer_.writeTimeStep( timer, state, prev_well_state ); // Stop timer and create timing report total_timer.stop(); SimulatorReport report; report.pressure_time = stime; report.transport_time = 0.0; report.total_time = total_timer.secsSinceStart(); report.total_newton_iterations = totalNonlinearIterations; report.total_linear_iterations = totalLinearIterations; return report; }
// ----------------- 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)."); } boost::scoped_ptr<EclipseGridParser> deck; boost::scoped_ptr<GridManager> grid; boost::scoped_ptr<BlackoilPropertiesInterface> props; boost::scoped_ptr<BlackoilPropsAdInterface> new_props; boost::scoped_ptr<RockCompressibility> rock_comp; BlackoilState 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"); deck.reset(new EclipseGridParser(deck_filename)); // Grid init grid.reset(new GridManager(*deck)); // use the capitalized part of the deck's filename between the // last '/' and the last '.' character as base name. std::string baseName = deck_filename; auto charPos = baseName.rfind('/'); if (charPos != std::string::npos) baseName = baseName.substr(charPos + 1); charPos = baseName.rfind('.'); if (charPos != std::string::npos) baseName = baseName.substr(0, charPos); baseName = boost::to_upper_copy(baseName); Opm::EclipseWriter outputWriter(param, share_obj(*deck), share_obj(*grid->c_grid())); // Rock and fluid init props.reset(new BlackoilPropertiesFromDeck(*deck, *grid->c_grid(), param)); new_props.reset(new BlackoilPropsAdFromDeck(*deck, *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)); // 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); initBlackoilSurfvol(*grid->c_grid(), *props, state); enum { Oil = BlackoilPhases::Liquid, Gas = BlackoilPhases::Vapour }; const PhaseUsage pu = props->phaseUsage(); 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 { initBlackoilStateFromDeck(*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; // Linear solver. LinearSolverFactory linsolver(param); // 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 (...) { OPM_THROW(std::runtime_error, "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"); } std::cout << "\n\n================ Starting main simulation loop ===============\n" << " (number of epochs: " << (deck->numberOfEpochs()) << ")\n\n" << std::flush; SimulatorReport rep; // With a deck, we may have more epochs etc. WellState well_state; int step = 0; SimulatorTimer simtimer; // Use timer for last epoch to obtain total time. deck->setCurrentEpoch(deck->numberOfEpochs() - 1); simtimer.init(*deck); const double total_time = simtimer.totalTime(); for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { // Set epoch index. deck->setCurrentEpoch(epoch); // Update the timer. if (deck->hasField("TSTEP")) { simtimer.init(*deck); } else { if (epoch != 0) { OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); } simtimer.init(param); } simtimer.setCurrentStepNum(step); simtimer.setTotalTime(total_time); // Report on start of epoch. std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" << "\n (number of steps: " << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, well_state WellsManager wells(*deck, *grid->c_grid(), props->permeability()); // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every epoch, // since number of wells may change etc. if (epoch == 0) { well_state.init(wells.c_wells(), state); } // Create and run simulator. SimulatorFullyImplicitBlackoil simulator(param, *grid->c_grid(), *new_props, rock_comp->isActive() ? rock_comp.get() : 0, wells, linsolver, grav, outputWriter); if (epoch == 0) { warnIfUnusedParams(param); } SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); if (output) { epoch_rep.reportParam(epoch_os); } // 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); if (output) { std::string filename = output_dir + "/walltime.param"; std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); rep.reportParam(tot_os); } } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; throw; }