Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
- Added IDA option to suppress algebraic variables in local error tests.
- Removed `COO_Matrix` class.
- Added portable `Vector` class and policy-based memory utilities.
- Add new `LinearSolver` interface for linear solvers.
- Added new `Rosenbrock` integrator.
- Clarified naming conventions for macros.

## v0.1

Expand Down
18 changes: 18 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ option(GridKit_ENABLE_SUNDIALS "Enable SUNDIALS support" OFF)
# Enzyme support is disabled by default
option(GridKit_ENABLE_ENZYME "Enable automatic differentiation with Enzyme" OFF)

# ReSolve support is disabled by default
option(GridKit_ENABLE_RESOLVE "Enable ReSolve support" OFF)

# Enable the address sanitizer
option(GridKit_ENABLE_ASAN "Enable the address sanitizer" OFF)

Expand All @@ -52,6 +55,7 @@ list(
IPOPT
SUNDIALS
ENZYME
RESOLVE
ASAN
UBSAN
OPENMP)
Expand Down Expand Up @@ -147,6 +151,20 @@ if(GRIDKIT_ENABLE_THREADS)
find_package(Threads REQUIRED)
endif()

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

# Macro that adds libraries
include(GridkitAddLibrary)

Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ name. Use all caps (screaming snake case).
constexpr double EXP = 2.7183 // Yes
```

### Macros

Macros should use all caps, same as constants.

### Enums (enumerated types)

Always define `enum`s inside `GridKit` namespace. The `enum` name should
Expand Down
1 change: 1 addition & 0 deletions GridKit/LinearAlgebra/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
add_subdirectory(SparseMatrix)
add_subdirectory(DenseMatrix)
add_subdirectory(Vector)
add_subdirectory(Solver)
16 changes: 8 additions & 8 deletions GridKit/LinearAlgebra/DenseMatrix/DenseMatrix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
#include <limits>
#include <vector>

/**
* @brief Class to provide dense matrices.
*
* @deprecated This class is deprecated. Use Vector(n, k) instead.
*
* This is intended for small matrices that store model Jacobians to be subsequently copied
* into large sparse matrices.
*/
namespace GridKit
{
namespace LinearAlgebra
{
/**
* @brief Class to provide dense matrices.
*
* @deprecated This class is deprecated. Use Vector(n, k) instead.
*
* This is intended for small matrices that store model Jacobians to be subsequently copied
* into large sparse matrices.
*/
template <typename RealT, typename IdxT>
class DenseMatrix
{
Expand Down
7 changes: 7 additions & 0 deletions GridKit/LinearAlgebra/Solver/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
if(GRIDKIT_ENABLE_RESOLVE)
gridkit_add_library(
resolve_system_solver
SOURCES ResolveSystemSolver.cpp
HEADERS ResolveSystemSolver.hpp
LINK_LIBRARIES PRIVATE ReSolve::ReSolve)
endif()
55 changes: 55 additions & 0 deletions GridKit/LinearAlgebra/Solver/LinearSolver.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#pragma once

#include <GridKit/LinearAlgebra/SparseMatrix/CsrMatrix.hpp>
#include <GridKit/ScalarTraits.hpp>

#include "GridKit/LinearAlgebra/Vector/Vector.hpp"

namespace GridKit
{
namespace LinearAlgebra
{

/**
* @brief An interface for linear solvers to be used in GridKit, such as in \ref Integrator::Rosenbrock.
*
*/
template <class ScalarT, typename IdxT>
class LinearSolver
{
public:
using RealT = GridKit::ScalarTraits<ScalarT>::RealT;

/**
* @brief Configure the solver by analyzing the given matrix. The sparsity pattern of the given matrix must be set, and future calls
* to other methods will use this sparsity pattern, as well as the data pointer of the given matrix.
*
* @param matrix The matrix to configure the solver with.
* @return int An error code, or 0 if none.
*/
virtual int configureSolver(GridKit::LinearAlgebra::CsrMatrix<RealT, IdxT>& matrix) = 0;
/**
* @brief Setup the solver with matrix data.
*
* @param reuse_factors If true, factors from a previous call to `setupSolver()` are reused.
* @pre \ref configureSolver() must be called first and every time the sparsity pattern of the matrix changes.
* @pre If `reuse_factors` is true, then `setupSolver` must have been called once before with the same sparsity pattern.
* @return int An error code, or 0 if none.
*/
virtual int setupSolver(bool reuse_factors = false) = 0;
/**
* @brief Perform a linear solve using the configured matrix.
*
* @param rhs The right-hand-side vector (input). No data is changed.
* @param lhs The left-hand-side vector (output).
* @pre \ref setupSolver must be called first and every time the data of the matrix changes.
* @return int An error code, or 0 if none.
*
* @todo The data of the `rhs` parameter doesn't change, but we can't mark as cosntant because ReSolve currently requires
* a non-`const` pointer for vectors, even if it will only read from that vector. In the future this might change, so this
* can be updated to be `const`.
*/
virtual int solve(GridKit::LinearAlgebra::Vector<ScalarT, IdxT>& rhs, GridKit::LinearAlgebra::Vector<ScalarT, IdxT>& lhs) = 0;
};
} // namespace LinearAlgebra
} // namespace GridKit
105 changes: 105 additions & 0 deletions GridKit/LinearAlgebra/Solver/ResolveSystemSolver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "ResolveSystemSolver.hpp"

#include "GridKit/MemoryUtilities/MemoryUtils.hpp"
#include <resolve/vector/Vector.hpp>

namespace GridKit
{
namespace LinearAlgebra
{
/**
* @brief Helper function to convert GridKit `MemorySpace` into ReSolve `MemorySpace`.
*
*/
ReSolve::memory::MemorySpace memorySpaceAsResolve(memory::MemorySpace memspace)
{
switch (memspace)
{
case memory::HOST:
return ReSolve::memory::HOST;
case memory::DEVICE:
return ReSolve::memory::DEVICE;
default:
throw "Memory space not supported";
}
}

template <class ScalarT, typename IdxT>
ResolveSystemSolver<ScalarT, IdxT>::ResolveSystemSolver(ReSolve::SystemSolver& lin_solver, GridKit::memory::MemorySpace memspace)
: lin_solver_(lin_solver), memspace_(memorySpaceAsResolve(memspace))
{
Comment on lines +29 to +30

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You really don't need this. The SystemSolver class will manage host/device memory spaces under the hood.

Suggested change
: lin_solver_(lin_solver), memspace_(memorySpaceAsResolve(memspace))
{
: lin_solver_(lin_solver)
{

In the PR the memspace_ is used only to set vector/matrix views. I would just mark those places as @todo to remove them when we improve linear algebra support in GridKit. Better to merge this PR now and address these issues in the next PR.

}

/**
* @brief Creates a new ReSolve matrix with the dimensions of the given matrix, then sets its data pointers to those of the given matrix.
* Sets up the linear solver to use this new ReSolve matrix.
*
* @todo Right now preconditioning doesn't work. There should be a ReSolve PR soon for this.
*
*/
template <class ScalarT, typename IdxT>
int ResolveSystemSolver<ScalarT, IdxT>::configureSolver(GridKit::LinearAlgebra::CsrMatrix<RealT, IdxT>& matrix)
{
matrix_ = std::make_unique<ReSolve::matrix::Csr>(matrix.getNumRows(), matrix.getNumColumns(), matrix.getNnz());

if (int err = matrix_->setDataPointers(matrix.getRowData(), matrix.getColData(), matrix.getValues(), memspace_))
return err;

if (int err = lin_solver_.setMatrix(matrix_.get()))
return err;

if (int err = lin_solver_.analyze())
return err;

// TODO: This function needs to be called to properly use a preconditioner in ReSolve (if there is any), but currently will error
// if there is no preconditioner configured. Once we can detect if a preconditioner is configured, we can restore this functionality.
// Also, we will always want to use *right* preconditioning.
// BUBBLE_FAIL(lin_solver_.preconditionerSetup("right"));

return 0;
}

template <class ScalarT, typename IdxT>
int ResolveSystemSolver<ScalarT, IdxT>::setupSolver(bool reuse_factors)
{
if (reuse_factors)
{
if (int err = lin_solver_.refactorize())
return err;
}
Comment on lines +65 to +69

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe there should be a third option reuse_pivot_sequence. Then, this conditional would look something like this:

Suggested change
if (reuse_factors)
{
if (int err = lin_solver_.refactorize())
return err;
}
if (reuse_factors)
{
return 0;
}
else if (reuse_pivot_sequence)
{
if (int err = lin_solver_.refactorize())
return err;
}

Might be a good idea to have an enum instead of multiple boolean flags.

else
{
if (int err = lin_solver_.factorize())
return err;
}

return 0;
}

/**
* @brief Perform the solver by creating two new ReSolve vectors and setting their data to point to their GridKit counterparts.
* Then ask ReSolve to perform the solve. Since the matrix should be pointing at the correct GridKit buffers and the vectors
* as well, this solve should correctly fill `lhs`'s data buffer.
*
*/
template <class ScalarT, typename IdxT>
int ResolveSystemSolver<ScalarT, IdxT>::solve(GridKit::LinearAlgebra::Vector<ScalarT, IdxT>& rhs, GridKit::LinearAlgebra::Vector<ScalarT, IdxT>& lhs)
{
ReSolve::vector::Vector resolve_rhs(rhs.getSize());
ReSolve::vector::Vector resolve_lhs(lhs.getSize());

if (int err = resolve_rhs.setData(rhs.getData(), memspace_))
return err;

if (int err = resolve_lhs.setData(lhs.getData(), memspace_))
return err;

if (int err = lin_solver_.solve(&resolve_rhs, &resolve_lhs))
return err;
Comment on lines +88 to +98

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like setData methods as they are ticking bomb but we still carry them along. How about we create a view constructor to make a shallow copy of the vector:

Suggested change
ReSolve::vector::Vector resolve_rhs(rhs.getSize());
ReSolve::vector::Vector resolve_lhs(lhs.getSize());
if (int err = resolve_rhs.setData(rhs.getData(), memspace_))
return err;
if (int err = resolve_lhs.setData(lhs.getData(), memspace_))
return err;
if (int err = lin_solver_.solve(&resolve_rhs, &resolve_lhs))
return err;
ReSolve::vector::Vector resolve_rhs(rhs.getSize(), rhs.getData());
ReSolve::vector::Vector resolve_lhs(lhs.getSize(), lhs.getData());
if (int err = lin_solver_.solve(&resolve_rhs, &resolve_lhs))
return err;

Even better would be if that constructor is made private and we implement method createVectorView that would invoke such constructor and create vector object that does not own the data.


return 0;
}

template class ResolveSystemSolver<double, int>;
} // namespace LinearAlgebra
} // namespace GridKit
45 changes: 45 additions & 0 deletions GridKit/LinearAlgebra/Solver/ResolveSystemSolver.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include <memory>

#include <GridKit/LinearAlgebra/Solver/LinearSolver.hpp>

#include <resolve/MemoryUtils.hpp>
#include <resolve/SystemSolver.hpp>
#include <resolve/matrix/Csr.hpp>

namespace GridKit
{
namespace LinearAlgebra
{
template <class ScalarT, typename IdxT>
class ResolveSystemSolver : public LinearSolver<ScalarT, IdxT>
{
public:
using RealT = LinearSolver<ScalarT, IdxT>::RealT;

ResolveSystemSolver(ReSolve::SystemSolver& lin_solver, GridKit::memory::MemorySpace memspace = GridKit::memory::HOST);

int configureSolver(GridKit::LinearAlgebra::CsrMatrix<RealT, IdxT>& matrix) final;
int setupSolver(bool reuse_factors = false) final;
int solve(GridKit::LinearAlgebra::Vector<ScalarT, IdxT>& rhs, GridKit::LinearAlgebra::Vector<ScalarT, IdxT>& lhs) final;

private:
/**
* @brief The ReSolve matrix used for the linear solve. Its data pointers should just point to a \ref GridKit::LinearAlgebra::CsrMatrix.
*
*/
std::unique_ptr<ReSolve::matrix::Csr> matrix_;
/**
* @brief The ReSolve linear solver to use.
*
*/
ReSolve::SystemSolver& lin_solver_;
/**
* @brief The memspace the data is stored in.
*
*/
ReSolve::memory::MemorySpace memspace_;
};
} // namespace LinearAlgebra
} // namespace GridKit
29 changes: 25 additions & 4 deletions GridKit/Solver/Dynamic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
# - Cameron Rutherford <cameron.rutherford@pnnl.gov>
#]]

set(_install_headers DynamicSolver.hpp Ida.hpp)

if(GRIDKIT_ENABLE_SUNDIALS_SPARSE)
gridkit_add_library(
solvers_dyn
SOURCES Ida.cpp
HEADERS ${_install_headers}
HEADERS Ida.hpp DynamicSolver.hpp
LINK_LIBRARIES
PUBLIC
SUNDIALS::nvecserial
Expand All @@ -29,7 +27,7 @@ else()
gridkit_add_library(
solvers_dyn
SOURCES Ida.cpp
HEADERS ${_install_headers}
HEADERS Ida.hpp DynamicSolver.hpp
LINK_LIBRARIES
PUBLIC
SUNDIALS::nvecserial
Expand All @@ -42,3 +40,26 @@ else()
PUBLIC
GridKit::sparse_matrix)
endif()

if(GRIDKIT_ENABLE_RESOLVE)
gridkit_add_library(
rosenbrock
SOURCES Rosenbrock.cpp RosenbrockTableaus.cpp
HEADERS Rosenbrock.hpp
Native/ErrorNorm.hpp
Native/StepControl.hpp
Native/StepController.hpp
LINK_LIBRARIES
PRIVATE
ReSolve::ReSolve
PUBLIC
GridKit::dense_vector
PUBLIC
GridKit::definitions
PUBLIC
GridKit::utilities_logger
PUBLIC
GridKit::sparse_matrix)
endif()

add_subdirectory(Native)
33 changes: 33 additions & 0 deletions GridKit/Solver/Dynamic/Native/AdaptiveStep.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "AdaptiveStep.hpp"

#include <algorithm>
#include <cmath>

#include <GridKit/Constants.hpp>

namespace AnalysisManager
{
namespace NativeDynamicSolver
{
/**
* @brief Standard textbook adaptive controller. Accept if `err <= 1` and use
*
* \f[h_{new} = h * \min \left\{fac_{max}, \max\left\{fac_{min}, fac_{scale} \cdot e ^{-1/p}\right\}\right\}.\f]
*
*/
template <typename RealT>
StepControl<RealT> AdaptiveStep<RealT>::nextStep(RealT err, StepControl<RealT> prev_step, uint8_t method_order)
{
StepControl<RealT> next_step = prev_step;

double h_mult = std::min(params_.fac_max_, std::max(params_.fac_scale_ * std::pow(err, GridKit::MINUS_ONE<RealT> / method_order), params_.fac_min_));

next_step.accept_ = err <= 1;
next_step.step_size_ *= h_mult;

return next_step;
}

template class AdaptiveStep<double>;
} // namespace NativeDynamicSolver
} // namespace AnalysisManager
Loading
Loading