void EquelleRuntimeCPU::output(const String& tag, const CollOfScalar& vals) { if (output_to_file_) { int count = -1; auto it = outputcount_.find(tag); if (it == outputcount_.end()) { count = 0; outputcount_[tag] = 1; // should contain the count to be used next time for same tag. } else { count = outputcount_[tag]; ++outputcount_[tag]; } std::ostringstream fname; fname << tag << "-" << std::setw(5) << std::setfill('0') << count << ".output"; std::ofstream file(fname.str().c_str()); if (!file) { OPM_THROW(std::runtime_error, "Failed to open " << fname.str()); } file.precision(16); std::copy(vals.value().data(), vals.value().data() + vals.size(), std::ostream_iterator<double>(file, "\n")); } else { std::cout << tag << " =\n"; for (int i = 0; i < vals.size(); ++i) { std::cout << std::setw(15) << std::left << ( vals.value()[i] ) << " "; } std::cout << std::endl; } }
CollOfScalar EquelleRuntimeCPU::singlePrimaryVariable(const CollOfScalar& initial_values) { std::vector<int> block_pattern; block_pattern.push_back(initial_values.size()); // Syntax below is: CollOfScalar::variable(block index, initialized from, block structure) return CollOfScalar::variable(0, initial_values.value(), block_pattern); }
CollOfScalar EquelleRuntimeCPU::solveForUpdate(const CollOfScalar& residual) const { Eigen::SparseMatrix<double, Eigen::RowMajor> matr = residual.derivative()[0]; CollOfScalar::V du = CollOfScalar::V::Zero(residual.size()); Opm::time::StopWatch clock; clock.start(); // solve(n, # nonzero values ("val"), ptr to col indices // ("col_ind"), ptr to row locations in val array ("row_ind") // (these two may be swapped, not sure about the naming convention // here...), array of actual values ("val") (I guess... '*sa'...), // rhs, solution) Opm::LinearSolverInterface::LinearSolverReport rep = linsolver_.solve(matr.rows(), matr.nonZeros(), matr.outerIndexPtr(), matr.innerIndexPtr(), matr.valuePtr(), residual.value().data(), du.data()); if (verbose_ > 2) { std::cout << " solveForUpdate: Linear solver took: " << clock.secsSinceLast() << " seconds." << std::endl; } if (!rep.converged) { OPM_THROW(std::runtime_error, "Linear solver convergence failure."); } return du; }
/// This function is not provided by AutoDiffBlock, so we must add it here. inline CollOfScalar sqrt(const CollOfScalar& x) { // d(sqrt(x))/dy = 1/(2*sqrt(x)) * dx/dy const auto& xjac = x.derivative(); if (xjac.empty()) { return CollOfScalar(sqrt(x.value())); } const int num_blocks = xjac.size(); std::vector<CollOfScalar::M> jac(num_blocks); const auto sqrt_x = sqrt(x.value()); const CollOfScalar::M one_over_two_sqrt_x((0.5/sqrt_x).matrix().asDiagonal()); for (int block = 0; block < num_blocks; ++block) { jac[block] = one_over_two_sqrt_x * xjac[block]; } return CollOfScalar::ADB::function(sqrt_x, jac); }
/// This operator is not provided by AutoDiffBlock, so we must add it here. inline CollOfBool operator==(const CollOfScalar& x, const CollOfScalar& y) { return x.value() == y.value(); }
/// This operator is not provided by AutoDiffBlock, so we must add it here. inline CollOfBool operator==(const CollOfScalar& x, const Scalar& s) { return x.value() == s; }
/// This operator is not provided by AutoDiffBlock, so we must add it here. inline CollOfBool operator==(const Scalar& s, const CollOfScalar& x) { return s == x.value(); }
CollOfScalar EquelleRuntimeCUDA::newtonSolve(const ResidualFunctor& rescomp, const CollOfScalar& u_initialguess) { Opm::time::StopWatch clock; clock.start(); // Set up Newton loop. // Define the primary variable CollOfScalar u = CollOfScalar(u_initialguess, true); if (verbose_ > 2) { output("Initial u", u); output(" newtonSolve: norm (initial u)", twoNorm(u)); } CollOfScalar residual = rescomp(u); if (verbose_ > 2) { output("Initial residual", residual); output(" newtonSolve: norm (initial residual)", twoNorm(residual)); } int iter = 0; // Debugging output not specified in Equelle. if (verbose_ > 1) { std::cout << " newtonSolve: iter = " << iter << " (max = " << max_iter_ << "), norm(residual) = " << twoNorm(residual) << " (tol = " << abs_res_tol_ << ")" << std::endl; } CollOfScalar du; // Execute newton loop until residual is small or we have used too many iterations. while ( (twoNorm(residual) > abs_res_tol_) && (iter < max_iter_) ) { if ( solver_.getSolver() == CPU ) { du = serialSolveForUpdate(residual); } else { // Solve linear equations for du, apply update. du = solver_.solve(residual.derivative(), residual.value(), verbose_); } // du is a constant, hence, u is still a primary variable with an identity // matrix as its derivative. u = u - du; // Recompute residual. residual = rescomp(u); if (verbose_ > 2) { // Debugging output not specified in Equelle. output("u", u); output(" newtonSolve: norm(u)", twoNorm(u)); output("residual", residual); output(" newtonSolve: norm(residual)", twoNorm(residual)); } ++iter; // Debugging output not specified in Equelle. if (verbose_ > 1) { std::cout << " newtonSolve: iter = " << iter << " (max = " << max_iter_ << "), norm(residual) = " << twoNorm(residual) << " (tol = " << abs_res_tol_ << ")" << std::endl; } } if (verbose_ > 0) { if (twoNorm(residual) > abs_res_tol_) { std::cout << "Newton solver failed to converge in " << max_iter_ << " iterations" << std::endl; } else { std::cout << "Newton solver converged in " << iter << " iterations" << std::endl; } } if (verbose_ > 1) { std::cout << "Newton solver took: " << clock.secsSinceLast() << " seconds." << std::endl; } return CollOfScalar(u.value()); }
double EquelleRuntimeCPU::twoNorm(const CollOfScalar& vals) const { return vals.value().matrix().norm(); }
Scalar EquelleRuntimeCPU::prodReduce(const CollOfScalar& x) const { return x.value().prod(); }
Scalar EquelleRuntimeCPU::sumReduce(const CollOfScalar& x) const { return x.value().sum(); }
Scalar EquelleRuntimeCPU::maxReduce(const CollOfScalar& x) const { return x.value().maxCoeff(); }