The poisson solver example..
This example depends on simple-solver.
Introduction
This example solves a 1D Poisson equation:
\(
u : [0, 1] \rightarrow R\\
u'' = f\\
u(0) = u0\\
u(1) = u1
\)
using a finite difference method on an equidistant grid with K discretization points (K can be controlled with a command line parameter). The discretization is done via the second order Taylor polynomial:
\(
u(x + h) = u(x) - u'(x)h + 1/2 u''(x)h^2 + O(h^3)\\
u(x - h) = u(x) + u'(x)h + 1/2 u''(x)h^2 + O(h^3) / +\\
---------------------- \\
-u(x - h) + 2u(x) + -u(x + h) = -f(x)h^2 + O(h^3)
\)
For an equidistant grid with K "inner" discretization points \(x1, ..., xk, \)and step size \( h = 1 / (K + 1)\), the formula produces a system of linear equations
\(
2u_1 - u_2 = -f_1 h^2 + u0\\
-u_(k-1) + 2u_k - u_(k+1) = -f_k h^2, k = 2, ..., K - 1\\
-u_(K-1) + 2u_K = -f_K h^2 + u1\\
\)
which is then solved using Ginkgo's implementation of the CG method preconditioned with block-Jacobi. It is also possible to specify on which executor Ginkgo will solve the system via the command line. The function \(`f` \)is set to \(`f(x) = 6x`\) (making the solution \(`u(x) = x^3`\)), but that can be changed in the main function.
The intention of the example is to show how Ginkgo can be used to build an application solving a real-world problem, which includes a solution of a large, sparse linear system as a component.
About the example
The commented program
row_ptrs[i + 1] = pos;
}
}
Generates the RHS vector given f and the boundary conditions.
template <typename Closure, typename ValueType>
void generate_rhs(Closure f, ValueType u0, ValueType u1,
{
const auto discretization_points = rhs->get_size()[0];
auto values = rhs->get_values();
const ValueType h = 1.0 / static_cast<ValueType>(discretization_points + 1);
const auto xi = static_cast<ValueType>(i + 1) * h;
values[i] = -f(xi) * h * h;
}
values[0] += u0;
values[discretization_points - 1] += u1;
}
std::size_t size_type
Definition types.hpp:89
Prints the solution u.
template <typename Closure, typename ValueType>
void print_solution(ValueType u0, ValueType u1,
{
std::cout << u0 << '\n';
for (int i = 0; i < u->get_size()[0]; ++i) {
}
std::cout << u1 << std::endl;
}
const value_type * get_const_values() const noexcept
Definition dense.hpp:859
Computes the 1-norm of the error given the computed u and the correct solution function correct_u.
template <typename Closure, typename ValueType>
Closure correct_u)
{
const ValueType h = 1.0 / static_cast<ValueType>(discretization_points + 1);
for (int i = 0; i < discretization_points; ++i) {
using std::abs;
const auto xi = static_cast<ValueType>(i + 1) * h;
error +=
}
return error;
}
int main(int argc, char* argv[])
{
typename detail::remove_complex_s< T >::type remove_complex
Definition math.hpp:260
Some shortcuts
using ValueType = double;
using IndexType = int;
Definition jacobi.hpp:189
Print version information
if (argc == 2 && (std::string(argv[1]) == "--help")) {
std::cerr << "Usage: " << argv[0]
<< " [executor] [DISCRETIZATION_POINTS]" << std::endl;
std::exit(-1);
}
static const version_info & get()
Definition version.hpp:139
Get number of discretization points
const unsigned int discretization_points =
argc >= 3 ? std::atoi(argv[2]) : 100;
Get the executor string
const auto executor_string = argc >= 2 ? argv[1] : "reference";
Figure out where to run the code
std::map<std::string, std::function<std::shared_ptr<gko::Executor>()>>
exec_map{
{"cuda",
[] {
}},
{"hip",
[] {
}},
{"dpcpp",
[] {
}},
{"reference", [] { return gko::ReferenceExecutor::create(); }}};
static std::shared_ptr< CudaExecutor > create(int device_id, std::shared_ptr< Executor > master, bool device_reset, allocation_mode alloc_mode=default_cuda_alloc_mode, CUstream_st *stream=nullptr)
static std::shared_ptr< DpcppExecutor > create(int device_id, std::shared_ptr< Executor > master, std::string device_type="all", dpcpp_queue_property property=dpcpp_queue_property::in_order)
static std::shared_ptr< HipExecutor > create(int device_id, std::shared_ptr< Executor > master, bool device_reset, allocation_mode alloc_mode=default_hip_alloc_mode, CUstream_st *stream=nullptr)
static std::shared_ptr< OmpExecutor > create(std::shared_ptr< CpuAllocatorBase > alloc=std::make_shared< CpuAllocator >())
Definition executor.hpp:1396
executor where Ginkgo will perform the computation
const auto exec = exec_map.at(executor_string)();
executor used by the application
const auto app_exec = exec->get_master();
Set up the problem: define the exact solution, the right hand side and the Dirichlet boundary condition.
auto correct_u = [](ValueType x) { return x * x * x; };
auto f = [](ValueType x) { return ValueType(6) * x; };
auto u0 = correct_u(0);
auto u1 = correct_u(1);
initialize matrix and vectors
auto matrix = mtx::create(app_exec,
gko::dim<2>(discretization_points),
3 * discretization_points - 2);
generate_stencil_matrix(matrix.get());
generate_rhs(f, u0, u1, rhs.get());
for (int i = 0; i < u->get_size()[0]; ++i) {
}
static std::unique_ptr< Dense > create(std::shared_ptr< const Executor > exec, const dim< 2 > &size={}, size_type stride=0)
value_type * get_values() noexcept
Definition dense.hpp:850
Generate solver and solve the system
cg::build()
.with_criteria(
gko::stop::Iteration::build().with_max_iters(discretization_points),
reduction_factor))
.with_preconditioner(bj::build())
.on(exec)
->generate(clone(exec, matrix))
->apply(rhs, u);
Definition residual_norm.hpp:113
Uncomment to print the solution print_solution<ValueType>(u0, u1, u.get());
std::cout << "Solve complete.\nThe average relative error is "
<< calculate_error(discretization_points, u.get(), correct_u) /
discretization_points)
<< std::endl;
}
Results
This is the expected output:
Solve complete.
The average relative error is 2.52236e-11
Comments about programming and debugging
The plain program
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <ginkgo/ginkgo.hpp>
template <typename ValueType, typename IndexType>
{
const auto discretization_points = matrix->get_size()[0];
int pos = 0;
const ValueType coefs[] = {-1, 2, -1};
row_ptrs[0] = pos;
for (int i = 0; i < discretization_points; ++i) {
for (auto ofs : {-1, 0, 1}) {
if (0 <= i + ofs && i + ofs < discretization_points) {
values[pos] = coefs[ofs + 1];
col_idxs[pos] = i + ofs;
++pos;
}
}
row_ptrs[i + 1] = pos;
}
}
template <typename Closure, typename ValueType>
void generate_rhs(Closure f, ValueType u0, ValueType u1,
{
const auto discretization_points =
rhs->get_size()[0];
auto values =
rhs->get_values();
const ValueType h = 1.0 / static_cast<ValueType>(discretization_points + 1);
const auto xi = static_cast<ValueType>(i + 1) * h;
values[i] = -f(xi) * h * h;
}
values[0] += u0;
values[discretization_points - 1] += u1;
}
template <typename Closure, typename ValueType>
void print_solution(ValueType u0, ValueType u1,
{
std::cout << u0 << '\n';
for (int i = 0; i < u->get_size()[0]; ++i) {
}
std::cout << u1 << std::endl;
}
template <typename Closure, typename ValueType>
Closure correct_u)
{
const ValueType h = 1.0 / static_cast<ValueType>(discretization_points + 1);
for (int i = 0; i < discretization_points; ++i) {
using std::abs;
const auto xi = static_cast<ValueType>(i + 1) * h;
error +=
}
return error;
}
int main(int argc, char* argv[])
{
using ValueType = double;
using IndexType = int;
if (argc == 2 && (std::string(argv[1]) == "--help")) {
std::cerr << "Usage: " << argv[0]
<< " [executor] [DISCRETIZATION_POINTS]" << std::endl;
std::exit(-1);
}
const unsigned int discretization_points =
argc >= 3 ? std::atoi(argv[2]) : 100;
const auto executor_string = argc >= 2 ? argv[1] : "reference";
std::map<std::string, std::function<std::shared_ptr<gko::Executor>()>>
exec_map{
{"cuda",
[] {
}},
{"hip",
[] {
}},
{"dpcpp",
[] {
}},
{"reference", [] { return gko::ReferenceExecutor::create(); }}};
const auto exec = exec_map.at(executor_string)();
const auto app_exec = exec->get_master();
auto correct_u = [](ValueType x) { return x * x * x; };
auto f = [](ValueType x) { return ValueType(6) * x; };
auto u0 = correct_u(0);
auto u1 = correct_u(1);
auto matrix = mtx::create(app_exec,
gko::dim<2>(discretization_points),
3 * discretization_points - 2);
generate_stencil_matrix(matrix.get());
generate_rhs(f, u0, u1,
rhs.get());
for (int i = 0; i < u->get_size()[0]; ++i) {
}
cg::build()
.with_criteria(
gko::stop::Iteration::build().with_max_iters(discretization_points),
reduction_factor))
.with_preconditioner(bj::build())
.on(exec)
->generate(
clone(exec, matrix))
->apply(rhs, u);
std::cout << "Solve complete.\nThe average relative error is "
<< calculate_error(discretization_points, u.get(), correct_u) /
discretization_points)
<< std::endl;
}
index_type * get_row_ptrs() noexcept
Definition csr.hpp:1005
value_type * get_values() noexcept
Definition csr.hpp:955
index_type * get_col_idxs() noexcept
Definition csr.hpp:986
constexpr std::enable_if_t<!is_complex_s< T >::value, T > abs(const T &x)
Definition math.hpp:931
detail::cloned_type< Pointer > clone(const Pointer &p)
Definition utils_helper.hpp:173