inline double UpscalerBase<Traits>::computeDelta(const int flow_dir) const { double side1_pos = 0.0; double side2_pos = 0.0; double side1_area = 0.0; double side2_area = 0.0; for (CellIter c = ginterf_.cellbegin(); c != ginterf_.cellend(); ++c) { for (FaceIter f = c->facebegin(); f != c->faceend(); ++f) { if (f->boundary()) { int canon_bid = bcond_.getCanonicalBoundaryId(f->boundaryId()); if ((canon_bid - 1)/2 == flow_dir) { double area = f->area(); double pos_comp = f->centroid()[flow_dir]; if (canon_bid - 1 == 2*flow_dir) { side1_pos += area*pos_comp; side1_area += area; } else { side2_pos += area*pos_comp; side2_area += area; } } } } } // delta is the average length. return side2_pos/side2_area - side1_pos/side1_area; }
void getInverseMatrix(const CellIter& c, FullMatrix<Scalar,SP,FortranOrdering>& Binv) const { // Binv = (N*lambda*K*N' + t*diag(A)*(I - Q*Q')*diag(A))/vol // ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ // precomputed: n_ precomputed: second_term_ // t = 6/dim * trace(lambda*K) int ci = c->index(); int nf = Binv.numRows(); ImmutableFortranMatrix n(nf, dim, &n_[ci][0]); ImmutableFortranMatrix t2(nf, nf, &second_term_[ci][0]); Binv = t2; ImmutableFortranMatrix lambdaK(dim, dim, lambdaK_.data()); SharedFortranMatrix T2(nf, dim, &t2_[0]); // T2 <- N*lambda*K matMulAdd_NN(Scalar(1.0), n, lambdaK, Scalar(0.0), T2); // Binv <- (T2*N' + t*Binv) / vol(c) // == (N*lambda*K*N' + t*(diag(A) * (I - Q*Q') * diag(A))) / vol(c) // // where t = 6/d * TRACE(lambda*K) (== 2*TRACE(lambda*K) for 3D). // Scalar t = Scalar(6.0) * trace(lambdaK) / dim; matMulAdd_NT(Scalar(1.0) / c->volume(), T2, n, t / c->volume(), Binv ); }
double UpscalerBase<Traits>::computeAverageVelocity(const FlowSol& flow_solution, const int flow_dir, const int pdrop_dir) const { double side1_flux = 0.0; double side2_flux = 0.0; double side1_area = 0.0; double side2_area = 0.0; int num_faces = 0; int num_bdyfaces = 0; int num_side1 = 0; int num_side2 = 0; for (CellIter c = ginterf_.cellbegin(); c != ginterf_.cellend(); ++c) { for (FaceIter f = c->facebegin(); f != c->faceend(); ++f) { ++num_faces; if (f->boundary()) { ++num_bdyfaces; int canon_bid = bcond_.getCanonicalBoundaryId(f->boundaryId()); if ((canon_bid - 1)/2 == flow_dir) { double flux = flow_solution.outflux(f); double area = f->area(); double norm_comp = f->normal()[flow_dir]; // std::cout << "bid " << f->boundaryId() << " area " << area << " n " << norm_comp << std::endl; if (canon_bid - 1 == 2*flow_dir) { ++num_side1; if (flow_dir == pdrop_dir && flux > 0.0) { #ifdef VERBOSE std::cerr << "Flow may be in wrong direction at bid: " << f->boundaryId()<<" (canonical: "<<canon_bid << ") Magnitude: " << std::fabs(flux) << std::endl; #endif // OPM_THROW(std::runtime_error, "Detected outflow at entry face: " << face); } side1_flux += flux*norm_comp; side1_area += area; } else { assert(canon_bid - 1 == 2*flow_dir + 1); ++num_side2; if (flow_dir == pdrop_dir && flux < 0.0) { #ifdef VERBOSE std::cerr << "Flow may be in wrong direction at bid: " << f->boundaryId() << " Magnitude: " << std::fabs(flux) << std::endl; #endif // OPM_THROW(std::runtime_error, "Detected inflow at exit face: " << face); } side2_flux += flux*norm_comp; side2_area += area; } } } } } // std::cout << "Faces: " << num_faces << " Boundary faces: " << num_bdyfaces // << " Side 1 faces: " << num_side1 << " Side 2 faces: " << num_side2 << std::endl; // q is the average velocity. return 0.5*(side1_flux/side1_area + side2_flux/side2_area); }
double UpscalerBase<Traits>::upscaleNTG() const { double total_vol = 0.0; double total_net_vol = 0.0; for (CellIter c = ginterf_.cellbegin(); c != ginterf_.cellend(); ++c) { total_vol += c->volume(); total_net_vol += c->volume()*res_prop_.ntg(c->index()); } return total_net_vol/total_vol; }
double UpscalerBase<Traits>::upscaleNetPorosity() const { double total_net_vol = 0.0; double total_pore_vol = 0.0; for (CellIter c = ginterf_.cellbegin(); c != ginterf_.cellend(); ++c) { total_net_vol += c->volume()*res_prop_.ntg(c->index()); total_pore_vol += c->volume()*res_prop_.porosity(c->index())*res_prop_.ntg(c->index()); } if (total_net_vol>0.0) return total_pore_vol/total_net_vol; else return 0.0; }
double SteadyStateUpscaler<Traits>::lastSaturationUpscaled() const { typedef typename GridInterface::CellIterator CellIter; double pore_vol = 0.0; double sat_vol = 0.0; for (CellIter c = this->ginterf_.cellbegin(); c != this->ginterf_.cellend(); ++c) { double cell_pore_vol = c->volume()*this->res_prop_.porosity(c->index()); pore_vol += cell_pore_vol; sat_vol += cell_pore_vol*last_saturation_state_[c->index()]; } // Dividing by pore volume gives average saturations. return sat_vol/pore_vol; }
std::pair<double, double> poreSatVolumes(const GridInterface& grid, const ReservoirProperties& rp, const std::vector<double>& sat) { typedef typename GridInterface::CellIterator CellIter; double pore_vol = 0.0; double sat_vol = 0.0; for (CellIter c = grid.cellbegin(); c != grid.cellend(); ++c) { double cell_pore_vol = c->volume()*rp.porosity(c->index()); pore_vol += cell_pore_vol; sat_vol += cell_pore_vol*sat[c->index()]; } // Dividing by pore volume gives average saturations. return std::make_pair(pore_vol, sat_vol); }
void computeDynamicParams(const CellIter& c, const FluidInterface& fl, const std::vector<Sat>& s) { const int ci = c->index(); std::array<Scalar, dim * dim> lambda_t; std::array<Scalar, dim * dim> pmob_data; SharedFortranMatrix pmob(dim, dim, &pmob_data[0]); SharedFortranMatrix Kg (dim, 1 , &Kg_[ci][0]); std::array<Scalar, FluidInterface::NumberOfPhases> rho; fl.phaseDensities(ci, rho); std::fill(dyn_Kg_.begin(), dyn_Kg_.end(), Scalar(0.0)); std::fill(lambda_t.begin(), lambda_t.end(), 0.0); for (int phase = 0; phase < FluidInterface::NumberOfPhases; ++phase) { fl.phaseMobility(phase, ci, s[ci], pmob); // dyn_Kg_ += (\rho_phase \lambda_phase) Kg vecMulAdd_N(rho[phase], pmob, Kg.data(), Scalar(1.0), dyn_Kg_.data()); // \lambda_t += \lambda_phase std::transform(lambda_t.begin(), lambda_t.end(), pmob_data.begin(), lambda_t.begin(), std::plus<Scalar>()); } // lambdaK_ = (\sum_i \lambda_i) K SharedFortranMatrix lambdaT(dim, dim, lambda_t.data()); SharedFortranMatrix lambdaK(dim, dim, lambdaK_.data()); prod(lambdaT, prock_->permeability(ci), lambdaK); }
double UpscalerBase<Traits>::upscaleSOWCR(const bool NTG) const { double total_sowcr = 0.0; double total_pore_vol = 0.0; if (NTG) { for (CellIter c = ginterf_.cellbegin(); c != ginterf_.cellend(); ++c) { total_sowcr += c->volume()*res_prop_.porosity(c->index())*res_prop_.ntg(c->index())*res_prop_.sowcr(c->index()); total_pore_vol += c->volume()*res_prop_.porosity(c->index())*res_prop_.ntg(c->index()); } } else { for (CellIter c = ginterf_.cellbegin(); c != ginterf_.cellend(); ++c) { total_sowcr += c->volume()*res_prop_.porosity(c->index())*res_prop_.sowcr(c->index()); total_pore_vol += c->volume()*res_prop_.porosity(c->index()); } } return total_sowcr/total_pore_vol; }
void gravityFlux(const CellIter& c, Vector& gflux) const { const int ci = c->index(); const int nf = n_.rowSize(ci) / dim; ImmutableFortranMatrix N(nf, dim, &n_[ci][0]); // gflux = N (\sum_i \rho_i \lambda_i) Kg vecMulAdd_N(Scalar(1.0), N, &dyn_Kg_[0], Scalar(0.0), &gflux[0]); }
/// @brief /// Main evaluation routine. Computes the inverse of the /// matrix representation of the mimetic inner product in a /// single cell with kown permeability @f$K@f$. Adds a /// regularization term in order to guarantee a positive /// definite matrix. /// /// @tparam RockInterface /// Type representing rock properties. Assumed to /// expose a method @code permeability(i) @endcode which /// retrieves the static permeability tensor of cell @code /// i @endcode. The permeability tensor, @$K@$, is in /// turn, assumed to expose a method @code operator()(int /// i, int j) @endcode such that the call @code K(i,j) /// @endcode retrieves the @f$ij@f$'th component of the /// cell permeability @f$K@f$. /// /// @param [in] c /// Cell for which to evaluate the inverse of the mimetic /// inner product. /// /// @param [in] r /// Specific reservoir properties. Only the permeability /// is used in method @code buildMatrix() @endcode. /// /// @param [in] nf /// Number of faces (i.e., number of neighbours) of cell /// @code *c @endcode. void buildStaticContrib(const CellIter& c, const RockInterface& r, const typename CellIter::Vector& grav, const int nf) { // Binv = (N*lambda*K*N' + t*diag(A)*(I - Q*Q')*diag(A))/vol // ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ // precompute: n_ precompute: second_term_ // t = 6/dim * trace(lambda*K) typedef typename CellIter::FaceIterator FI; typedef typename CellIter::Vector CV; typedef typename FI ::Vector FV; // Now we need to remember the rocks, since we will need // the permeability for dynamic assembly. prock_ = &r; const int ci = c->index(); static_assert (FV::dimension == int(dim), ""); assert (int(t1_.size()) >= nf * dim); assert (int(t2_.size()) >= nf * dim); assert (int(fa_.size()) >= nf * nf); SharedFortranMatrix T2 (nf, dim, &t2_ [0]); SharedFortranMatrix fa (nf, nf , &fa_ [0]); SharedFortranMatrix second_term(nf, nf, &second_term_[ci][0]); SharedFortranMatrix n(nf, dim, &n_[ci][0]); // Clear matrices of any residual data. zero(second_term); zero(n); zero(T2); zero(fa); // Setup: second_term <- I, n <- N, T2 <- C const CV cc = c->centroid(); int i = 0; for (FI f = c->facebegin(); f != c->faceend(); ++f, ++i) { second_term(i,i) = Scalar(1.0); fa(i,i) = f->area(); FV fc = f->centroid(); fc -= cc; fc *= fa(i,i); FV fn = f->normal (); fn *= fa(i,i); for (int j = 0; j < dim; ++j) { n (i,j) = fn[j]; T2(i,j) = fc[j]; } } assert (i == nf); // T2 <- orth(T2) if (orthogonalizeColumns(T2) != 0) { assert (false); } // second_term <- second_term - T2*T2' == I - Q*Q' symmetricUpdate(Scalar(-1.0), T2, Scalar(1.0), second_term); // second_term <- diag(A) * second_term * diag(A) symmetricUpdate(fa, second_term); // Gravity term: Kg_ = K * grav vecMulAdd_N(Scalar(1.0), r.permeability(ci), &grav[0], Scalar(0.0), &Kg_[ci][0]); }
void SteadyStateUpscaler<Traits>::computeInOutFlows(std::pair<double, double>& water_inout, std::pair<double, double>& oil_inout, const FlowSol& flow_solution, const std::vector<double>& saturations) const { typedef typename GridInterface::CellIterator CellIter; typedef typename CellIter::FaceIterator FaceIter; double side1_flux = 0.0; double side2_flux = 0.0; double side1_flux_oil = 0.0; double side2_flux_oil = 0.0; std::map<int, double> frac_flow_by_bid; int num_cells = this->ginterf_.numberOfCells(); std::vector<double> cell_inflows_w(num_cells, 0.0); std::vector<double> cell_outflows_w(num_cells, 0.0); // Two passes: First pass, deal with outflow, second pass, deal with inflow. // This is for the periodic case, so that we are sure all fractional flows have // been set in frac_flow_by_bid. for (int pass = 0; pass < 2; ++pass) { for (CellIter c = this->ginterf_.cellbegin(); c != this->ginterf_.cellend(); ++c) { for (FaceIter f = c->facebegin(); f != c->faceend(); ++f) { if (f->boundary()) { double flux = flow_solution.outflux(f); const SatBC& sc = this->bcond_.satCond(f); if (flux < 0.0 && pass == 1) { // This is an inflow face. double frac_flow = 0.0; if (sc.isPeriodic()) { assert(sc.saturationDifference() == 0.0); int partner_bid = this->bcond_.getPeriodicPartner(f->boundaryId()); std::map<int, double>::const_iterator it = frac_flow_by_bid.find(partner_bid); if (it == frac_flow_by_bid.end()) { OPM_THROW(std::runtime_error, "Could not find periodic partner fractional flow. Face bid = " << f->boundaryId() << " and partner bid = " << partner_bid); } frac_flow = it->second; } else { assert(sc.isDirichlet()); frac_flow = this->res_prop_.fractionalFlow(c->index(), sc.saturation()); } cell_inflows_w[c->index()] += flux*frac_flow; side1_flux += flux*frac_flow; side1_flux_oil += flux*(1.0 - frac_flow); } else if (flux >= 0.0 && pass == 0) { // This is an outflow face. double frac_flow = this->res_prop_.fractionalFlow(c->index(), saturations[c->index()]); if (sc.isPeriodic()) { frac_flow_by_bid[f->boundaryId()] = frac_flow; // std::cout << "Inserted bid " << f->boundaryId() << std::endl; } cell_outflows_w[c->index()] += flux*frac_flow; side2_flux += flux*frac_flow; side2_flux_oil += flux*(1.0 - frac_flow); } } } } } water_inout = std::make_pair(side1_flux, side2_flux); oil_inout = std::make_pair(side1_flux_oil, side2_flux_oil); }