User Guide

Getting started

Re::Solve is maintained and developed at Github. It has following build dependencies:

  • C++11 compliant compiler

  • CMake >= 3.22

  • KLU, AMD and COLAMD libraries from SuiteSparse >= 5.0 (optional)

  • CUDA >= 11.4 (optional)

  • HIP/ROCm >= 6.4 (optional)

To acquire and build Re::Solve it is as easy as:

$ git clone https://code.ornl.gov/peles/resolve.git
$ mkdir build && cd build
$ cmake ../resolve
$ make

Note if you don’t include any of the optional dependencies, there will be little functionality provided. You might want to use tool such as ccmake to enable features you need.

To install the library

In the directory where you built the library run

$ make install

To change the install location please use the CMakePresets.json file as mentioned in test and deploy

To run it, download test linear systems and then try some of Re::Solve’s examples. The examples will emulate nonlinear solver calling the linear solver repeatedly.

To use the Re::Solve library in your own project

Make sure Resolve library is installed (see above)

Below is an example CMakeList.txt file to use Re::Solve library in your project

cmake_minimum_required(VERSION 3.20)
project(my_app LANGUAGES CXX)

find_package(ReSolve CONFIG
  PATHS ${ReSolve_DIR} ${ReSolve_DIR}/share/resolve/cmake
  ENV LD_LIBRARY_PATH ENV DYLD_LIBRARY_PATH
  REQUIRED)

# Build your executable
add_executable(my_app my_app.cpp)
target_link_libraries(my_app PRIVATE ReSolve::ReSolve)

Test and Deploy

Re::Solve as a library is tested every pull request via GitHub pipelines that execute various library tests including a test of Re::Solve being consumed as package within an external project as mentioned in Using Re::Solve in your own Project

To test your own install of Re::Solve simply cd into your Re::Solve build directory and run

$ make test

or

$ ctest

Below is an example of what a functional Re::Solve build will ouput on Passing tests

Test project /people/dane678/resolve/build
    Start 1: resolve_consume
1/9 Test #1: resolve_consume ..................   Passed   16.51 sec
    Start 2: klu_klu_test
2/9 Test #2: klu_klu_test .....................   Passed    1.04 sec
    Start 3: klu_rf_test
3/9 Test #3: klu_rf_test ......................   Passed    1.04 sec
    Start 4: klu_rf_fgmres_test
4/9 Test #4: klu_rf_fgmres_test ...............   Passed    3.14 sec
    Start 5: klu_glu_test
5/9 Test #5: klu_glu_test .....................   Passed    1.06 sec
    Start 6: matrix_test
6/9 Test #6: matrix_test ......................   Passed    0.03 sec
    Start 7: matrix_handler_test
7/9 Test #7: matrix_handler_test ..............   Passed    0.97 sec
    Start 8: vector_handler_test
8/9 Test #8: vector_handler_test ..............   Passed    0.98 sec
    Start 9: logger_test
9/9 Test #9: logger_test ......................   Passed    0.03 sec

Important Notes

You can find default Cmake Configurations in the CMakePresets.json file, which allows for easy switching between different CMake Configs. To create your own CMake Configuration we encourage you to utlize a CmakeUserPresets.json file. To learn more about cmake-presets please checkout the cmake docs.

For example if you wanted to build and install Re::Solve on a High Performance Computing Cluster such as ORNL’s Frontier we encourage you to utilize our cluster preset. Using this preset will set CMAKE_INSTALL_PREFIX to an install folder. To use this preset simply call the preset flag in the cmake build step.

cmake -B build --preset cluster

Getting Help

For any questions or to report a bug please submit a GitHub issue.

Re::Solve General API Description

Users are encouraged to look at the examples and tests for guidance on how to use the library. When there is any conflict between the examples/tests and this documentation, the examples/tests are correct.

Solver Subroutines

For direct solvers, the following functions are provided and must be called in this order:

  • analyze() - Performs symbolic factorization, currently only with KLU on the CPU.

  • factorize() - Performs numeric factorization, given a symbolic factorization.

  • refactorizationSetup() - Prepares for a new numeric factorization, given a new matrix with the same sparsity structure (required only if using refactorize()).

  • solve() - Solves the linear system, given a numeric factorization.

  • refactorize() - Reuses the symbolic factorization to perform a new numeric factorization, given a new matrix.

Subsequent systems only require calls to refactorize() and solve() if the sparsity structure hasn’t changed.

Note some examples do not reuse the factorization from the 0-th iteration because the numeric sparsity structure changes and that’s how the matrices are stored. In a practical application, the sparsity structure is likely to remain the same. If it does not, it is the user’s responsibility to reallocate memory and call analyze() and factorize() again. The user should enforce that symbolic nonzeros are stored explicitly, even if they are numerically zero.

Function Requirements

Functions must be used as in the examples and tests:

  • Workspaces are required for GPU solvers. The generic workspace_type is required for backend agnostic code.

  • Handles are created with the initializeHandles() function and destroyed with the default destructor.

  • Allocate memory first, preferably with the ReSolve::MatrixHandler or ReSolve::VectorHandler classes.

  • Memory must be allocated before attempting to copy to it.

  • Solver output variables must be allocated before passing to the solver.

  • Use setup functions to initialize objects, where available. Do not call them more than once.

  • Call the set or factor functions before the corresponding reset or refactor functions.

  • Deallocate memory at the end.

Functions in ReSolve that take pointers as arguments will have the following requirements:

  • Pointers must be valid and point to allocated memory, unless the function’s use is to allocate memory.

  • Pointers that are not marked as const must point to memory that is writable.

  • Pointers that are marked as const must point to memory that is readable.

Memory Synchronization

  • For CPU solvers, memory is always in sync.

  • For GPU solvers, the user must synchronize memory manually.

  • Manually call setUpdated() if you modify the contents of a ReSolve::MatrixHandler or ReSolve::VectorHandler object without using the object’s member functions.

  • Manually call syncData(memspace) when you modify the other memory space and want them to match. Both memory spaces must be allocated.