inline typename UpscalerBase<Traits>::permtensor_t UpscalerBase<Traits>::upscaleEffectivePerm(const FluidInterface& fluid) { int num_cells = ginterf_.numberOfCells(); // No source or sink. std::vector<double> src(num_cells, 0.0); // Just water. std::vector<double> sat(num_cells, 1.0); // Gravity. Dune::FieldVector<double, 3> gravity(0.0); // gravity[2] = -Dune::unit::gravity; permtensor_t upscaled_K(3, 3, (double*)0); for (int pdd = 0; pdd < Dimension; ++pdd) { setupUpscalingConditions(ginterf_, bctype_, pdd, 1.0, 1.0, twodim_hack_, bcond_); if (pdd == 0) { // Only on first iteration, since we do not change the // structure of the system, the way the flow solver is // implemented. flow_solver_.init(ginterf_, res_prop_, gravity, bcond_); } // Run pressure solver. bool same_matrix = (bctype_ != Fixed) && (pdd != 0); flow_solver_.solve(fluid, sat, bcond_, src, residual_tolerance_, linsolver_verbosity_, linsolver_type_, same_matrix, linsolver_maxit_, linsolver_prolongate_factor_, linsolver_smooth_steps_); double max_mod = flow_solver_.postProcessFluxes(); std::cout << "Max mod = " << max_mod << std::endl; // Compute upscaled K. double Q[Dimension] = { 0 }; switch (bctype_) { case Fixed: Q[pdd] = computeAverageVelocity(flow_solver_.getSolution(), pdd, pdd); break; case Linear: case Periodic: for (int i = 0; i < Dimension; ++i) { Q[i] = computeAverageVelocity(flow_solver_.getSolution(), i, pdd); } break; default: OPM_THROW(std::runtime_error, "Unknown boundary type: " << bctype_); } double delta = computeDelta(pdd); for (int i = 0; i < Dimension; ++i) { upscaled_K(i, pdd) = Q[i] * delta; } } return upscaled_K; }
inline void setupBoundaryConditions(const Opm::parameter::ParameterGroup& param, const GridInterface& g, BCs& bcs) { if (param.getDefault("upscaling", false)) { int bct = param.get<int>("boundary_condition_type"); int pddir = param.getDefault("pressure_drop_direction", 0); double pdrop = param.getDefault("boundary_pressuredrop", 1.0e5); double bdy_sat = param.getDefault("boundary_saturation", 1.0); bool twodim_hack = param.getDefault("2d_hack", false); setupUpscalingConditions(g, bct, pddir, pdrop, bdy_sat, twodim_hack, bcs); return; } if (param.getDefault("region_based_bcs", false)) { setupRegionBasedConditions(param, g, bcs); return; } // Make flow equation boundary conditions. // Default is pressure 1.0e5 on the left, 0.0 on the right. // Recall that the boundary ids range from 1 to 6 for the cartesian edges, // and that boundary id 0 means interiour face/intersection. std::string flow_bc_type = param.getDefault<std::string>("flow_bc_type", "dirichlet"); FlowBC::BCType bct = FlowBC::Dirichlet; double leftval = 1.0*Opm::unit::barsa; double rightval = 0.0; if (flow_bc_type == "neumann") { bct = FlowBC::Neumann; leftval = param.get<double>("left_flux"); rightval = param.getDefault<double>("right_flux", -leftval); } else if (flow_bc_type == "dirichlet") { leftval = param.getDefault<double>("left_pressure", leftval); rightval = param.getDefault<double>("right_pressure", rightval); } else if (flow_bc_type == "periodic") { THROW("Periodic conditions not here yet."); } else { THROW("Unknown flow boundary condition type " << flow_bc_type); } bcs.resize(7); bcs.flowCond(1) = FlowBC(bct, leftval); bcs.flowCond(2) = FlowBC(bct, rightval); // Default transport boundary conditions are used. }
inline std::pair<typename SteadyStateUpscaler<Traits>::permtensor_t, typename SteadyStateUpscaler<Traits>::permtensor_t> SteadyStateUpscaler<Traits>:: upscaleSteadyState(const int flow_direction, const std::vector<double>& initial_saturation, const double boundary_saturation, const double pressure_drop, const permtensor_t& upscaled_perm) { static int count = 0; ++count; int num_cells = this->ginterf_.numberOfCells(); // No source or sink. std::vector<double> src(num_cells, 0.0); Opm::SparseVector<double> injection(num_cells); // Gravity. Dune::FieldVector<double, 3> gravity(0.0); if (use_gravity_) { gravity[2] = Opm::unit::gravity; } if (gravity.two_norm() > 0.0) { OPM_MESSAGE("Warning: Gravity is experimental for flow solver."); } // Set up initial saturation profile. std::vector<double> saturation = initial_saturation; // Set up boundary conditions. setupUpscalingConditions(this->ginterf_, this->bctype_, flow_direction, pressure_drop, boundary_saturation, this->twodim_hack_, this->bcond_); // Set up solvers. if (flow_direction == 0) { this->flow_solver_.init(this->ginterf_, this->res_prop_, gravity, this->bcond_); } transport_solver_.initObj(this->ginterf_, this->res_prop_, this->bcond_); // Run pressure solver. this->flow_solver_.solve(this->res_prop_, saturation, this->bcond_, src, this->residual_tolerance_, this->linsolver_verbosity_, this->linsolver_type_, false, this->linsolver_maxit_, this->linsolver_prolongate_factor_, this->linsolver_smooth_steps_); double max_mod = this->flow_solver_.postProcessFluxes(); std::cout << "Max mod = " << max_mod << std::endl; // Do a run till steady state. For now, we just do some pressure and transport steps... std::vector<double> saturation_old = saturation; for (int iter = 0; iter < simulation_steps_; ++iter) { // Run transport solver. transport_solver_.transportSolve(saturation, stepsize_, gravity, this->flow_solver_.getSolution(), injection); // Run pressure solver. this->flow_solver_.solve(this->res_prop_, saturation, this->bcond_, src, this->residual_tolerance_, this->linsolver_verbosity_, this->linsolver_type_, false, this->linsolver_maxit_, this->linsolver_prolongate_factor_, this->linsolver_smooth_steps_); max_mod = this->flow_solver_.postProcessFluxes(); std::cout << "Max mod = " << max_mod << std::endl; // Print in-out flows if requested. if (print_inoutflows_) { std::pair<double, double> w_io, o_io; computeInOutFlows(w_io, o_io, this->flow_solver_.getSolution(), saturation); std::cout << "Pressure step " << iter << "\nWater flow [in] " << w_io.first << " [out] " << w_io.second << "\nOil flow [in] " << o_io.first << " [out] " << o_io.second << std::endl; } // Output. if (output_vtk_) { writeVtkOutput(this->ginterf_, this->res_prop_, this->flow_solver_.getSolution(), saturation, std::string("output-steadystate") + '-' + boost::lexical_cast<std::string>(count) + '-' + boost::lexical_cast<std::string>(flow_direction) + '-' + boost::lexical_cast<std::string>(iter)); } // Comparing old to new. int num_cells = saturation.size(); double maxdiff = 0.0; for (int i = 0; i < num_cells; ++i) { maxdiff = std::max(maxdiff, std::fabs(saturation[i] - saturation_old[i])); } #ifdef VERBOSE std::cout << "Maximum saturation change: " << maxdiff << std::endl; #endif if (maxdiff < sat_change_threshold_) { #ifdef VERBOSE std::cout << "Maximum saturation change is under steady state threshold." << std::endl; #endif break; } // Copy to old. saturation_old = saturation; } // Compute phase mobilities. // First: compute maximal mobilities. typedef typename Super::ResProp::Mobility Mob; Mob m; double m1max = 0; double m2max = 0; for (int c = 0; c < num_cells; ++c) { this->res_prop_.phaseMobility(0, c, saturation[c], m.mob); m1max = maxMobility(m1max, m.mob); this->res_prop_.phaseMobility(1, c, saturation[c], m.mob); m2max = maxMobility(m2max, m.mob); } // Second: set thresholds. const double mob1_abs_thres = relperm_threshold_ / this->res_prop_.viscosityFirstPhase(); const double mob1_rel_thres = m1max / maximum_mobility_contrast_; const double mob1_threshold = std::max(mob1_abs_thres, mob1_rel_thres); const double mob2_abs_thres = relperm_threshold_ / this->res_prop_.viscositySecondPhase(); const double mob2_rel_thres = m2max / maximum_mobility_contrast_; const double mob2_threshold = std::max(mob2_abs_thres, mob2_rel_thres); // Third: extract and threshold. std::vector<Mob> mob1(num_cells); std::vector<Mob> mob2(num_cells); for (int c = 0; c < num_cells; ++c) { this->res_prop_.phaseMobility(0, c, saturation[c], mob1[c].mob); thresholdMobility(mob1[c].mob, mob1_threshold); this->res_prop_.phaseMobility(1, c, saturation[c], mob2[c].mob); thresholdMobility(mob2[c].mob, mob2_threshold); } // Compute upscaled relperm for each phase. ReservoirPropertyFixedMobility<Mob> fluid_first(mob1); permtensor_t eff_Kw = Super::upscaleEffectivePerm(fluid_first); ReservoirPropertyFixedMobility<Mob> fluid_second(mob2); permtensor_t eff_Ko = Super::upscaleEffectivePerm(fluid_second); // Set the steady state saturation fields for eventual outside access. last_saturation_state_.swap(saturation); // Compute the (anisotropic) upscaled mobilities. // eff_Kw := lambda_w*K // => lambda_w = eff_Kw*inv(K); permtensor_t lambda_w(matprod(eff_Kw, inverse3x3(upscaled_perm))); permtensor_t lambda_o(matprod(eff_Ko, inverse3x3(upscaled_perm))); // Compute (anisotropic) upscaled relative permeabilities. // lambda = k_r/mu permtensor_t k_rw(lambda_w); k_rw *= this->res_prop_.viscosityFirstPhase(); permtensor_t k_ro(lambda_o); k_ro *= this->res_prop_.viscositySecondPhase(); return std::make_pair(k_rw, k_ro); }