mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Added some utility functions for the CPR preconditioner.
The new functions are: - eliminateVariable() - recoverVariable() - isDiagonal()
This commit is contained in:
parent
3e41bf11e9
commit
c60a080a73
@ -27,6 +27,39 @@
|
|||||||
namespace Opm
|
namespace Opm
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
typedef AutoDiffBlock<double> ADB;
|
||||||
|
typedef ADB::V V;
|
||||||
|
typedef ADB::M M;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/// Eliminate a variable via Schur complement.
|
||||||
|
/// \param[in] eqs set of equations with Jacobians
|
||||||
|
/// \param[in] n index of equation/variable to eliminate.
|
||||||
|
/// \return new set of equations, one smaller than eqs.
|
||||||
|
/// Note: this method requires the eliminated variable to have the same size
|
||||||
|
/// as the equation in the corresponding position (that also will be eliminated).
|
||||||
|
/// It also required the jacobian block n of equation n to be diagonal.
|
||||||
|
std::vector<ADB> eliminateVariable(const std::vector<ADB>& eqs, const int n);
|
||||||
|
|
||||||
|
/// Recover that value of a variable previously eliminated.
|
||||||
|
/// \param[in] equation previously eliminated equation.
|
||||||
|
/// \param[in] partial_solution solution to the remainder system after elimination.
|
||||||
|
/// \param[in] n index of equation/variable that was eliminated.
|
||||||
|
/// \return solution to complete system.
|
||||||
|
V recoverVariable(const ADB& equation, const V& partial_solution, const int n);
|
||||||
|
|
||||||
|
/// Determine diagonality of a sparse matrix.
|
||||||
|
/// If there are off-diagonal elements in the sparse
|
||||||
|
/// structure, this function returns true if they are all
|
||||||
|
/// equal to zero.
|
||||||
|
bool isDiagonal(const M& matrix);
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Construct a system solver.
|
/// Construct a system solver.
|
||||||
/// \param[in] linsolver linear solver to use
|
/// \param[in] linsolver linear solver to use
|
||||||
NewtonIterationBlackoilCPR::NewtonIterationBlackoilCPR(const parameter::ParameterGroup& param)
|
NewtonIterationBlackoilCPR::NewtonIterationBlackoilCPR(const parameter::ParameterGroup& param)
|
||||||
@ -37,6 +70,10 @@ namespace Opm
|
|||||||
linsolver_full_.reset(new LinearSolverFactory(cpr_full));
|
linsolver_full_.reset(new LinearSolverFactory(cpr_full));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Solve the linear system Ax = b, with A being the
|
/// Solve the linear system Ax = b, with A being the
|
||||||
/// combined derivative matrix of the residual and b
|
/// combined derivative matrix of the residual and b
|
||||||
/// being the residual itself.
|
/// being the residual itself.
|
||||||
@ -69,5 +106,148 @@ namespace Opm
|
|||||||
return dx;
|
return dx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<ADB> eliminateVariable(const std::vector<ADB>& eqs, const int n)
|
||||||
|
{
|
||||||
|
// Check that the variable index to eliminate is within bounds.
|
||||||
|
const int num_eq = eqs.size();
|
||||||
|
const int num_vars = eqs[0].derivative().size();
|
||||||
|
if (num_eq != num_vars) {
|
||||||
|
OPM_THROW(std::logic_error, "eliminateVariable() requires the same number of variables and equations.");
|
||||||
|
}
|
||||||
|
if (n >= num_eq) {
|
||||||
|
OPM_THROW(std::logic_error, "Trying to eliminate variable from too small set of equations.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schur complement of (A B ; C D) wrt. D is A - B*inv(D)*C.
|
||||||
|
// This is applied to all 2x2 block submatrices.
|
||||||
|
// We require that D is diagonal.
|
||||||
|
const M& D = eqs[n].derivative()[n];
|
||||||
|
if (!isDiagonal(D)) {
|
||||||
|
// std::cout << "++++++++++++++++++++++++++++++++++++++++++++\n"
|
||||||
|
// << D
|
||||||
|
// << "++++++++++++++++++++++++++++++++++++++++++++\n" << std::endl;
|
||||||
|
OPM_THROW(std::logic_error, "Cannot do Schur complement with respect to non-diagonal block.");
|
||||||
|
}
|
||||||
|
V diag = D.diagonal();
|
||||||
|
Eigen::DiagonalMatrix<double, Eigen::Dynamic> invD = (1.0 / diag).matrix().asDiagonal();
|
||||||
|
std::vector<V> vals(num_eq); // Number n will remain empty.
|
||||||
|
std::vector<std::vector<M>> jacs(num_eq); // Number n will remain empty.
|
||||||
|
for (int eq = 0; eq < num_eq; ++eq) {
|
||||||
|
if (eq == n) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
jacs[eq].reserve(num_eq - 1);
|
||||||
|
const M& B = eqs[eq].derivative()[n];
|
||||||
|
for (int var = 0; var < num_eq; ++var) {
|
||||||
|
if (var == n) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Create new jacobians.
|
||||||
|
M schur_jac = eqs[eq].derivative()[var] - B * (invD * eqs[n].derivative()[var]);
|
||||||
|
jacs[eq].push_back(schur_jac);
|
||||||
|
}
|
||||||
|
// Update right hand side.
|
||||||
|
vals[eq] = eqs[eq].value().matrix() - B * (invD * eqs[n].value().matrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create return value.
|
||||||
|
std::vector<ADB> retval;
|
||||||
|
retval.reserve(num_eq - 1);
|
||||||
|
for (int eq = 0; eq < num_eq; ++eq) {
|
||||||
|
if (eq == n) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
retval.push_back(ADB::function(vals[eq], jacs[eq]));
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
V recoverVariable(const ADB& equation, const V& partial_solution, const int n)
|
||||||
|
{
|
||||||
|
// The equation to solve for the unknown y (to be recovered) is
|
||||||
|
// Cx + Dy = b
|
||||||
|
// y = inv(D) (b - Cx)
|
||||||
|
// where D is the eliminated block, C is the jacobian of
|
||||||
|
// the eliminated equation with respect to the
|
||||||
|
// non-eliminated unknowms, b is the right-hand side of
|
||||||
|
// the eliminated equation, and x is the partial solution
|
||||||
|
// of the non-eliminated unknowns.
|
||||||
|
// We require that D is diagonal.
|
||||||
|
|
||||||
|
// Find inv(D).
|
||||||
|
const M& D = equation.derivative()[n];
|
||||||
|
if (!isDiagonal(D)) {
|
||||||
|
OPM_THROW(std::logic_error, "Cannot do Schur complement with respect to non-diagonal block.");
|
||||||
|
}
|
||||||
|
V diag = D.diagonal();
|
||||||
|
Eigen::DiagonalMatrix<double, Eigen::Dynamic> invD = (1.0 / diag).matrix().asDiagonal();
|
||||||
|
|
||||||
|
// Build C.
|
||||||
|
std::vector<M> C_jacs = equation.derivative();
|
||||||
|
C_jacs.erase(C_jacs.begin() + n);
|
||||||
|
ADB eq_coll = collapseJacs(ADB::function(equation.value(), C_jacs));
|
||||||
|
const M& C = eq_coll.derivative()[0];
|
||||||
|
|
||||||
|
// Compute value of eliminated variable.
|
||||||
|
V elim_var = invD * (equation.value().matrix() - C * partial_solution.matrix());
|
||||||
|
|
||||||
|
// Find the relevant sizes to use when reconstructing the full solution.
|
||||||
|
const int nelim = equation.size();
|
||||||
|
const int npart = partial_solution.size();
|
||||||
|
assert(C.cols() == npart);
|
||||||
|
const int full_size = nelim + npart;
|
||||||
|
int start = 0;
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
start += equation.derivative()[i].cols();
|
||||||
|
}
|
||||||
|
assert(start < full_size);
|
||||||
|
|
||||||
|
// Reconstruct complete solution vector.
|
||||||
|
V sol(full_size);
|
||||||
|
std::copy_n(partial_solution.data(), start, sol.data());
|
||||||
|
std::copy_n(elim_var.data(), nelim, sol.data() + start);
|
||||||
|
std::copy_n(partial_solution.data() + start, npart - start, sol.data() + start + nelim);
|
||||||
|
return sol;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool isDiagonal(const M& matr)
|
||||||
|
{
|
||||||
|
M matrix = matr;
|
||||||
|
matrix.makeCompressed();
|
||||||
|
for (int k = 0; k < matrix.outerSize(); ++k) {
|
||||||
|
for (M::InnerIterator it(matrix, k); it; ++it) {
|
||||||
|
if (it.col() != it.row()) {
|
||||||
|
// Off-diagonal element.
|
||||||
|
if (it.value() != 0.0) {
|
||||||
|
// Nonzero off-diagonal element.
|
||||||
|
// std::cout << "off-diag: " << it.row() << ' ' << it.col() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
} // namespace Opm
|
} // namespace Opm
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user