/// Compute the output. void IncompTpfa::computeResults(SimulatorState& state, WellState& well_state) const { // Make sure h_ contains the direct-solution matrix // and right hand side (not jacobian and residual). // TODO: optimize by only adjusting b and diagonal of A. UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_); ifs_tpfa_assemble(gg, &forces_, &trans_[0], &gpress_omegaweighted_[0], h_); // Make sure h_->x contains the direct solution vector. assert(int(state.pressure().size()) == grid_.number_of_cells); assert(int(state.faceflux().size()) == grid_.number_of_faces); std::copy(state.pressure().begin(), state.pressure().end(), h_->x); std::copy(well_state.bhp().begin(), well_state.bhp().end(), h_->x + grid_.number_of_cells); // Obtain solution. ifs_tpfa_solution soln = { NULL, NULL, NULL, NULL }; soln.cell_press = &state.pressure()[0]; soln.face_flux = &state.faceflux()[0]; if (wells_ != NULL) { assert(int(well_state.bhp().size()) == wells_->number_of_wells); assert(int(well_state.perfRates().size()) == wells_->well_connpos[ wells_->number_of_wells ]); soln.well_flux = &well_state.perfRates()[0]; soln.well_press = &well_state.bhp()[0]; } ifs_tpfa_press_flux(gg, &forces_, &trans_[0], h_, &soln); // TODO: Check what parts of h_ are used here. }
// Solve with no rock compressibility (linear eqn). void IncompTpfa::solveIncomp(const double dt, SimulatorState& state, WellState& well_state) { // Set up properties. computePerSolveDynamicData(dt, state, well_state); // Assemble. UnstructuredGrid* gg = const_cast<UnstructuredGrid*>(&grid_); int ok = ifs_tpfa_assemble(gg, &forces_, &trans_[0], &gpress_omegaweighted_[0], h_); if (!ok) { OPM_THROW(std::runtime_error, "Failed assembling pressure system."); } // Solve. linsolver_.solve(h_->A, h_->b, h_->x); // Obtain solution. assert(int(state.pressure().size()) == grid_.number_of_cells); assert(int(state.faceflux().size()) == grid_.number_of_faces); ifs_tpfa_solution soln = { NULL, NULL, NULL, NULL }; soln.cell_press = &state.pressure()[0]; soln.face_flux = &state.faceflux()[0]; if (wells_ != NULL) { assert(int(well_state.bhp().size()) == wells_->number_of_wells); assert(int(well_state.perfRates().size()) == wells_->well_connpos[ wells_->number_of_wells ]); soln.well_flux = &well_state.perfRates()[0]; soln.well_press = &well_state.bhp()[0]; } ifs_tpfa_press_flux(gg, &forces_, &trans_[0], h_, &soln); }
double PIDTimeStepControl:: computeTimeStepSize( const double dt, const int /* iterations */, const SimulatorState& state ) const { const std::size_t pSize = p0_.size(); assert( state.pressure().size() == pSize ); const std::size_t satSize = sat0_.size(); assert( state.saturation().size() == satSize ); // compute u^n - u^n+1 for( std::size_t i=0; i<pSize; ++i ) { p0_[ i ] -= state.pressure()[ i ]; } for( std::size_t i=0; i<satSize; ++i ) { sat0_[ i ] -= state.saturation()[ i ]; } // compute || u^n - u^n+1 || const double stateOld = euclidianNormSquared( p0_.begin(), p0_.end() ) + euclidianNormSquared( sat0_.begin(), sat0_.end() ); // compute || u^n+1 || const double stateNew = euclidianNormSquared( state.pressure().begin(), state.pressure().end() ) + euclidianNormSquared( state.saturation().begin(), state.saturation().end() ); // shift errors for( int i=0; i<2; ++i ) { errors_[ i ] = errors_[i+1]; } // store new error const double error = stateOld / stateNew; errors_[ 2 ] = error ; if( error > tol_ ) { // adjust dt by given tolerance const double newDt = dt * tol_ / error; if( verbose_ ) std::cout << "Computed step size (tol): " << unit::convert::to( newDt, unit::day ) << " (days)" << std::endl; return newDt; } else { // values taking from turek time stepping paper const double kP = 0.075 ; const double kI = 0.175 ; const double kD = 0.01 ; const double newDt = (dt * std::pow( errors_[ 1 ] / errors_[ 2 ], kP ) * std::pow( tol_ / errors_[ 2 ], kI ) * std::pow( errors_[0]*errors_[0]/errors_[ 1 ]/errors_[ 2 ], kD )); if( verbose_ ) std::cout << "Computed step size (pow): " << unit::convert::to( newDt, unit::day ) << " (days)" << std::endl; return newDt; } }
/// Compute per-iteration dynamic properties. void IncompTpfa::computePerIterationDynamicData(const double /*dt*/, const SimulatorState& state, const WellState& well_state) { // These are the variables that get computed by this function: // // std::vector<double> porevol_ // std::vector<double> rock_comp_ // std::vector<double> pressures_ computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), porevol_); if (rock_comp_props_ && rock_comp_props_->isActive()) { for (int cell = 0; cell < grid_.number_of_cells; ++cell) { rock_comp_[cell] = rock_comp_props_->rockComp(state.pressure()[cell]); } } if (wells_) { std::copy(state.pressure().begin(), state.pressure().end(), pressures_.begin()); std::copy(well_state.bhp().begin(), well_state.bhp().end(), pressures_.begin() + grid_.number_of_cells); } }
/// Compute the residual in h_->b and Jacobian in h_->A. void IncompTpfa::assemble(const double dt, const SimulatorState& state, const WellState& /*well_state*/) { const double* pressures = wells_ ? &pressures_[0] : &state.pressure()[0]; bool ok = ifs_tpfa_assemble_comprock_increment(const_cast<UnstructuredGrid*>(&grid_), &forces_, &trans_[0], &gpress_omegaweighted_[0], &porevol_[0], &rock_comp_[0], dt, pressures, &initial_porevol_[0], h_); if (!ok) { OPM_THROW(std::runtime_error, "Failed assembling pressure system."); } }
void restorePressureData(const ecl_file_type* file, EclipseStateConstPtr eclipse_state, int numcells, SimulatorState& simulator_state) { const char* pressure = "PRESSURE"; if (ecl_file_has_kw(file , pressure)) { ecl_kw_type* pressure_kw = ecl_file_iget_named_kw(file, pressure, 0); if (ecl_kw_get_size(pressure_kw) != numcells) { throw std::runtime_error("Read of restart file: Could not restore pressure data, length of data from file not equal number of cells"); } float* pressure_data = ecl_kw_get_float_ptr(pressure_kw); const double deck_pressure_unit = (eclipse_state->getDeckUnitSystem().getType() == UnitSystem::UNIT_TYPE_METRIC) ? Opm::unit::barsa : Opm::unit::psia; for (size_t index = 0; index < simulator_state.pressure().size(); ++index) { simulator_state.pressure()[index] = unit::convert::from((double)pressure_data[index], deck_pressure_unit); } } else { throw std::runtime_error("Read of restart file: File does not contain PRESSURE data\n"); } }
bool SimulatorState::equals (const SimulatorState& other, double epsilon) const { bool equal = (num_phases_ == other.num_phases_); // if we use &=, then all the tests will be run regardless equal = equal && vectorApproxEqual( pressure() , other.pressure() , epsilon); equal = equal && vectorApproxEqual( temperature() , other.temperature() , epsilon); equal = equal && vectorApproxEqual( facepressure() , other.facepressure() , epsilon); equal = equal && vectorApproxEqual( faceflux() , other.faceflux() , epsilon); equal = equal && vectorApproxEqual( saturation() , other.saturation() , epsilon); return equal; }
/// Compute per-solve dynamic properties. void IncompTpfa::computePerSolveDynamicData(const double /*dt*/, const SimulatorState& state, const WellState& /*well_state*/) { // Computed here: // // std::vector<double> wdp_; // std::vector<double> totmob_; // std::vector<double> omega_; // std::vector<double> trans_; // std::vector<double> gpress_omegaweighted_; // std::vector<double> initial_porevol_; // ifs_tpfa_forces forces_; // wdp_ if (wells_) { Opm::computeWDP(*wells_, grid_, state.saturation(), props_.density(), gravity_ ? gravity_[2] : 0.0, true, wdp_); } // totmob_, omega_, gpress_omegaweighted_ if (gravity_) { computeTotalMobilityOmega(props_, allcells_, state.saturation(), totmob_, omega_); mim_ip_density_update(grid_.number_of_cells, grid_.cell_facepos, &omega_[0], &gpress_[0], &gpress_omegaweighted_[0]); } else { computeTotalMobility(props_, allcells_, state.saturation(), totmob_); } // trans_ tpfa_eff_trans_compute(const_cast<UnstructuredGrid*>(&grid_), &totmob_[0], &htrans_[0], &trans_[0]); // initial_porevol_ if (rock_comp_props_ && rock_comp_props_->isActive()) { computePorevolume(grid_, props_.porosity(), *rock_comp_props_, state.pressure(), initial_porevol_); } // forces_ forces_.src = src_.empty() ? NULL : &src_[0]; forces_.bc = bcs_; forces_.W = wells_; forces_.totmob = &totmob_[0]; forces_.wdp = wdp_.empty() ? NULL : &wdp_[0]; }
// Solve with rock compressibility (nonlinear eqn). void IncompTpfa::solveRockComp(const double dt, SimulatorState& state, WellState& well_state) { // This function is identical to CompressibleTpfa::solve(). // \TODO refactor? const int nc = grid_.number_of_cells; const int nw = (wells_) ? wells_->number_of_wells : 0; // Set up dynamic data. computePerSolveDynamicData(dt, state, well_state); computePerIterationDynamicData(dt, state, well_state); // Assemble J and F. assemble(dt, state, well_state); double inc_norm = 0.0; int iter = 0; double res_norm = residualNorm(); std::cout << "\nIteration Residual Change in p\n" << std::setw(9) << iter << std::setw(18) << res_norm << std::setw(18) << '*' << std::endl; while ((iter < maxiter_) && (res_norm > residual_tol_)) { // Solve for increment in Newton method: // incr = x_{n+1} - x_{n} = -J^{-1}F // (J is Jacobian matrix, F is residual) solveIncrement(); ++iter; // Update pressure vars with increment. for (int c = 0; c < nc; ++c) { state.pressure()[c] += h_->x[c]; } for (int w = 0; w < nw; ++w) { well_state.bhp()[w] += h_->x[nc + w]; } // Stop iterating if increment is small. inc_norm = incrementNorm(); if (inc_norm <= change_tol_) { std::cout << std::setw(9) << iter << std::setw(18) << '*' << std::setw(18) << inc_norm << std::endl; break; } // Set up dynamic data. computePerIterationDynamicData(dt, state, well_state); // Assemble J and F. assemble(dt, state, well_state); // Update residual norm. res_norm = residualNorm(); std::cout << std::setw(9) << iter << std::setw(18) << res_norm << std::setw(18) << inc_norm << std::endl; } if ((iter == maxiter_) && (res_norm > residual_tol_) && (inc_norm > change_tol_)) { OPM_THROW(std::runtime_error, "IncompTpfa::solve() failed to converge in " << maxiter_ << " iterations."); } std::cout << "Solved pressure in " << iter << " iterations." << std::endl; // Compute fluxes and face pressures. computeResults(state, well_state); }
void PIDTimeStepControl::initialize( const SimulatorState& state ) { // store current state for later time step computation p0_ = state.pressure(); sat0_ = state.saturation(); }