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);
}