/*
  Copyright 2015 SINTEF ICT, Applied Mathematics.
  This file is part of the Open Porous Media project (OPM).
  OPM is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  OPM is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with OPM.  If not, see .
*/
#include "config.h"
#include 
#include 
#include 
#include 
// #include 
// #include 
#include 
#include 
// #include 
// #include 
#include 
// #include 
#include 
// #include 
// #include 
// #include 
// #include 
namespace Opm
{
    /// Construct solver for incompressible case.
    /// \param[in] grid             A 2d or 3d grid.
    /// \param[in] props            Rock and fluid properties.
    /// \param[in] linsolver        Linear solver to use.
    /// \param[in] wells            The wells used as driving forces.
    IncompTpfaSinglePhase::IncompTpfaSinglePhase(const UnstructuredGrid& grid,
                                                 const IncompPropertiesSinglePhase& props,
                                                 const LinearSolverInterface& linsolver,
                                                 const Wells& wells)
        : grid_(grid),
          props_(props),
          linsolver_(linsolver),
          wells_(wells),
          htrans_(grid.cell_facepos[ grid.number_of_cells ]),
          trans_ (grid.number_of_faces),
          zeros_(grid.cell_facepos[ grid.number_of_cells ])
    {
        computeStaticData();
    }
    /// Destructor.
    IncompTpfaSinglePhase::~IncompTpfaSinglePhase()
    {
        ifs_tpfa_destroy(h_);
    }
    /// Solve the pressure equation.
    void IncompTpfaSinglePhase::solve(std::vector& press,
                                      std::vector& flux,
                                      std::vector& bhp,
                                      std::vector& wellrates)
    {
        // Set up properties.
        computePerSolveDynamicData();
        // Assemble.
        UnstructuredGrid* gg = const_cast(&grid_);
        int ok = ifs_tpfa_assemble(gg, &forces_, trans_.data(), zeros_.data(), h_);
        if (!ok) {
            OPM_THROW(std::runtime_error, "Failed assembling pressure system.");
        }
        // Solve.
        linsolver_.solve(h_->A, h_->b, h_->x);
        // Obtain solution.
        press.resize(grid_.number_of_cells);
        flux.resize(grid_.number_of_faces);
        wellrates.resize(wells_.well_connpos[ wells_.number_of_wells ]);
        bhp.resize(wells_.number_of_wells);
        ifs_tpfa_solution soln = { NULL, NULL, NULL, NULL };
        soln.cell_press = press.data();
        soln.face_flux  = flux.data();
        soln.well_press = bhp.data();
        soln.well_flux = wellrates.data();
        ifs_tpfa_press_flux(gg, &forces_, &trans_[0], h_, &soln);
    }
    /// Compute data that never changes (after construction).
    void IncompTpfaSinglePhase::computeStaticData()
    {
        UnstructuredGrid* gg = const_cast(&grid_);
        tpfa_htrans_compute(gg, props_.permeability(), &htrans_[0]);
        h_ = ifs_tpfa_construct(gg, const_cast(&wells_));
    }
    /// Compute per-solve dynamic properties.
    void IncompTpfaSinglePhase::computePerSolveDynamicData()
    {
        // Computed here:
        //
        // std::vector totmob_;
        // std::vector trans_;
        // ifs_tpfa_forces forces_;
        // totmob_
        totmob_.clear();
        totmob_.resize(grid_.number_of_cells, 1.0/(*props_.viscosity()));
        // trans_
        tpfa_eff_trans_compute(const_cast(&grid_), totmob_.data(), htrans_.data(), trans_.data());
        // forces_
        forces_.src = NULL;
        forces_.bc = NULL;
        forces_.W = &wells_;
        forces_.totmob = totmob_.data();
        forces_.wdp = zeros_.data();
    }
} // namespace Opm