Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backware Euler with vectorizable matrix types #596

Merged
merged 45 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
533cd3b
starting to test all solver parameter types
K20shores Jul 1, 2024
b871832
saving progress
K20shores Jul 1, 2024
3d6326c
saving progress
K20shores Jul 1, 2024
7cea304
testing all stages analytically
K20shores Jul 1, 2024
8686982
updating all interfaces
K20shores Jul 1, 2024
de78a91
correcting cuda build I hope
K20shores Jul 1, 2024
f26ce9f
testing jit against hires, e5, oregonator
K20shores Jul 1, 2024
6c635d2
adding cuda solver builder test
K20shores Jul 1, 2024
c8dda48
removing hires, e5, oregonator from cuda tests; they need their own k…
K20shores Jul 2, 2024
3bd4d72
testing e5 from a configuration
K20shores Jul 2, 2024
282c91e
testing e5 jit integration
K20shores Jul 2, 2024
d1636d1
testing e5 properly
K20shores Jul 3, 2024
be784d2
removing reset of L and U matrices (#594)
K20shores Jul 3, 2024
24de4b0
oregonator from a configuration
K20shores Jul 3, 2024
d1fed16
Merge branch '577-test-all-parameter-types-of-the-dense-matrix-cpu-ro…
K20shores Jul 3, 2024
0341d99
renaming things
K20shores Jul 3, 2024
68cbc61
using different tolerances?
K20shores Jul 3, 2024
f537eae
moving state onto and off of host
K20shores Jul 3, 2024
467a56e
saving gpu changes
K20shores Jul 3, 2024
98f52a4
Merge branch '577-test-all-parameter-types-of-the-dense-matrix-cpu-ro…
K20shores Jul 3, 2024
9bc7b34
updating cuda tests
K20shores Jul 5, 2024
dd9095f
adding some better tolerances for cuda tests
K20shores Jul 5, 2024
05b918d
adding different tolerances for e5
K20shores Jul 5, 2024
60a9ab0
adding citation to e5
K20shores Jul 5, 2024
54b1dbb
thing
K20shores Jul 5, 2024
133000c
Merge branch '577-test-all-parameter-types-of-the-dense-matrix-cpu-ro…
K20shores Jul 5, 2024
65ccaaf
formed hires equations
K20shores Jul 5, 2024
d01625a
using passing tolerances for cpu tests
K20shores Jul 8, 2024
470aa83
jit tolerances
K20shores Jul 8, 2024
4c81b13
backward euler tests
K20shores Jul 8, 2024
1f920c3
configuration for hires
K20shores Jul 8, 2024
4a4e130
add AddToDiagonal function on sparse matrix
mattldawson Jul 9, 2024
cdfe9fb
use ForEach in Backward Euler
mattldawson Jul 9, 2024
6ac759a
add convergence check function to backward euler
mattldawson Jul 9, 2024
3bfc196
Merge branch 'main' into develop-500-vector-euler
mattldawson Jul 9, 2024
d25f91f
fix merge problems
mattldawson Jul 10, 2024
25e52f4
add vector matrix to analytical solver tests
mattldawson Jul 11, 2024
3f9f142
update JIT analytical tests
mattldawson Jul 11, 2024
6887eac
set up general use analytical test function
mattldawson Jul 11, 2024
9a9049f
add general function for stiff analytical tests
mattldawson Jul 11, 2024
9e3f88b
fix jit analytical tests
mattldawson Jul 11, 2024
676198d
update remaining analytical tests
mattldawson Jul 12, 2024
223dfbc
address review comments
mattldawson Jul 12, 2024
41c6c7d
update cuda analytical tests
mattldawson Jul 15, 2024
0792e9d
update tolerances for cuda analytical tests
mattldawson Jul 16, 2024
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
2 changes: 1 addition & 1 deletion include/micm/configure/solver_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ namespace micm
"Ea is specified when C is also specified for an Arrhenius reaction. Pick one." };
}
// Calculate 'C' using 'Ea'
parameters.C_ = -1 * object["Ea"].get<double>() / BOLTZMANN_CONSTANT;
parameters.C_ = -1 * object["Ea"].get<double>() / constants::BOLTZMANN_CONSTANT;
}
arrhenius_rate_arr_.push_back(ArrheniusRateConstant(parameters));
std::unique_ptr<ArrheniusRateConstant> rate_ptr = std::make_unique<ArrheniusRateConstant>(parameters);
Expand Down
3 changes: 2 additions & 1 deletion include/micm/jit/solver/jit_linear_solver.inl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ namespace micm
if (matrix.NumberOfBlocks() != L || matrix.GroupVectorSize() != L)
{
std::string msg =
"JIT functions require the number of grid cells solved together to match the vector dimension template parameter, "
"JIT functions require the number of grid cells solved together (" +
std::to_string(matrix.NumberOfBlocks()) + ") to match the vector dimension template parameter, "
"currently: " +
std::to_string(L);
throw std::system_error(make_error_code(MicmJitErrc::InvalidMatrix), msg);
Expand Down
3 changes: 2 additions & 1 deletion include/micm/jit/solver/jit_lu_decomposition.inl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ namespace micm
if (matrix.NumberOfBlocks() > L)
{
std::string msg =
"JIT functions require the number of grid cells solved together to match the vector dimension template parameter, "
"JIT functions require the number of grid cells solved together (" +
std::to_string(matrix.NumberOfBlocks()) + ") to match the vector dimension template parameter, "
"currently: " +
std::to_string(L);
throw std::system_error(make_error_code(MicmJitErrc::InvalidMatrix), msg);
Expand Down
3 changes: 2 additions & 1 deletion include/micm/jit/solver/jit_rosenbrock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ namespace micm
if (jacobian.GroupVectorSize() != jacobian.NumberOfBlocks())
{
std::string msg =
"JIT functions require the number of grid cells solved together to match the vector dimension template "
"JIT functions require the number of grid cells solved together (" +
std::to_string(jacobian.NumberOfBlocks()) + ") to match the vector dimension template "
"parameter, currently: " +
std::to_string(jacobian.GroupVectorSize());
throw std::system_error(make_error_code(MicmJitErrc::InvalidMatrix), msg);
Expand Down
4 changes: 2 additions & 2 deletions include/micm/process/branched_rate_constant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ namespace micm

inline BranchedRateConstant::BranchedRateConstant(const BranchedRateConstantParameters& parameters)
: parameters_(parameters),
k0_(2.0e-22 * AVOGADRO_CONSTANT * 1.0e-6 * std::exp(parameters_.n_)),
z_(A(293.0, 2.45e19 / AVOGADRO_CONSTANT * 1.0e6) * (1.0 - parameters_.a0_) / parameters_.a0_)
k0_(2.0e-22 * constants::AVOGADRO_CONSTANT * 1.0e-6 * std::exp(parameters_.n_)),
z_(A(293.0, 2.45e19 / constants::AVOGADRO_CONSTANT * 1.0e6) * (1.0 - parameters_.a0_) / parameters_.a0_)
{
}

Expand Down
4 changes: 2 additions & 2 deletions include/micm/process/surface_rate_constant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ namespace micm

inline SurfaceRateConstant::SurfaceRateConstant(const SurfaceRateConstantParameters& parameters)
: parameters_(parameters),
diffusion_coefficient_(parameters.species_.GetProperty<double>(GAS_DIFFUSION_COEFFICIENT)),
mean_free_speed_factor_(8.0 * GAS_CONSTANT / (M_PI * parameters.species_.GetProperty<double>(MOLECULAR_WEIGHT)))
diffusion_coefficient_(parameters.species_.GetProperty<double>(keys::GAS_DIFFUSION_COEFFICIENT)),
mean_free_speed_factor_(8.0 * constants::GAS_CONSTANT / (M_PI * parameters.species_.GetProperty<double>(keys::MOLECULAR_WEIGHT)))
{
}

Expand Down
17 changes: 16 additions & 1 deletion include/micm/solver/backward_euler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <micm/solver/state.hpp>
#include <micm/system/system.hpp>
#include <micm/util/jacobian.hpp>
#include <micm/util/matrix.hpp>

#include <algorithm>
#include <array>
Expand Down Expand Up @@ -66,7 +67,21 @@ namespace micm
/// @param time_step Time [s] to advance the state by
/// @param state The state to advance
/// @return result of the solver (success or failure, and statistics)
SolverResult Solve(double time_step, auto& state);
SolverResult Solve(double time_step, auto& state) const;

/// @brief Determines whether the residual is small enough to stop the
/// internal solver iteration
/// @param parameters Solver parameters
/// @param residual The residual to check
/// @param state The current state being solved for
/// @return true if the residual is small enough to stop the iteration
template<class DenseMatrixPolicy>
static bool IsConverged(const BackwardEulerSolverParameters& parameters, const DenseMatrixPolicy& residual, const DenseMatrixPolicy& state)
requires(!VectorizableDense<DenseMatrixPolicy>);
template<class DenseMatrixPolicy>
static bool IsConverged(const BackwardEulerSolverParameters& parameters, const DenseMatrixPolicy& residual, const DenseMatrixPolicy& state)
requires(VectorizableDense<DenseMatrixPolicy>);

};

} // namespace micm
Expand Down
142 changes: 88 additions & 54 deletions include/micm/solver/backward_euler.inl
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,22 @@ inline std::error_code make_error_code(MicmBackwardEulerErrc e)
namespace micm
{
template<class RatesPolicy, class LinearSolverPolicy>
inline SolverResult BackwardEuler<RatesPolicy, LinearSolverPolicy>::Solve(double time_step, auto& state)
inline SolverResult BackwardEuler<RatesPolicy, LinearSolverPolicy>::Solve(double time_step, auto& state) const
{
// A fully implicit euler implementation is given by the following equation:
// y_{n+1} = y_n + H * f(t_{n+1}, y_{n+1})
// This is a root finding problem because you need to know y_{n+1} to compute f(t_{n+1}, y_{n+1})
// you need to solve the equation y_{n+1} - y_n - H f(t_{n+1}, y_{n+1}) = 0
// We will also use the same logic used by cam-chem to determine the time step
// That scheme is this:
// Start with H = time_step
// if that fails, try H = H/2 several times
// if that fails, try H = H/10 once
// if that fails, accept the current H but do not update the Yn vector
// the number of time step reduction is controlled by the time_step_reductions parameter
// A series of time step reductions are used after failed solves to try to find a solution
// These reductions are controlled by the time_step_reductions parameter in the solver parameters
// if the last attempt to reduce the timestep fails,
// accept the current H but do not update the Yn vector

SolverResult result;

double small = parameters_.small;
double small = parameters_.small_;
K20shores marked this conversation as resolved.
Show resolved Hide resolved
std::size_t max_iter = parameters_.max_number_of_steps_;
const auto time_step_reductions = parameters_.time_step_reductions;
const auto time_step_reductions = parameters_.time_step_reductions_;

double H = time_step;
double t = 0.0;
Expand Down Expand Up @@ -89,46 +86,30 @@ namespace micm
// after the first iteration Yn1 is updated to the new solution
// so we can use Yn1 to calculate the forcing and jacobian
// calculate forcing
std::fill(forcing.AsVector().begin(), forcing.AsVector().end(), 0.0);
forcing.Fill(0.0);
rates_.AddForcingTerms(state.rate_constants_, Yn1, forcing);
result.stats_.function_calls_++;

// calculate jacobian
std::fill(state.jacobian_.AsVector().begin(), state.jacobian_.AsVector().end(), 0.0);
// calculate the negative jacobian
state.jacobian_.Fill(0.0);
rates_.SubtractJacobianTerms(state.rate_constants_, Yn1, state.jacobian_);
result.stats_.jacobian_updates_++;

// subtract the inverse of the time step from the diagonal
// TODO: handle vectorized jacobian matrix
for (auto& jac : state.jacobian_.AsVector())
{
jac *= -1;
}
for (std::size_t i_block = 0; i_block < state.jacobian_.NumberOfBlocks(); ++i_block)
{
auto jacobian_vector = std::next(state.jacobian_.AsVector().begin(), i_block * state.jacobian_.FlatBlockSize());
for (const auto& i_elem : jacobian_diagonal_elements_)
jacobian_vector[i_elem] -= 1 / H;
}

// add the inverse of the time step from the diagonal
state.jacobian_.AddToDiagonal(1 / H);
K20shores marked this conversation as resolved.
Show resolved Hide resolved

// We want to solve this equation for a zero
// (y_{n+1} - y_n) / H = f(t_{n+1}, y_{n+1})

// try to find the root by factoring and solving the linear system
linear_solver_.Factor(state.jacobian_, state.lower_matrix_, state.upper_matrix_, singular);
result.stats_.decompositions_++;

auto yn1_iter = Yn1.begin();
auto yn_iter = Yn.begin();
auto forcing_iter = forcing.begin();
// forcing_blk in camchem
// residual = (Yn1 - Yn) / H - forcing;
// residual = forcing - (Yn1 - Yn) / H
// since forcing is only used once, we can reuse it to store the residual
for (; yn1_iter != Yn1.end(); ++yn1_iter, ++yn_iter, ++forcing_iter)
{
*forcing_iter = (*yn1_iter - *yn_iter) / H - *forcing_iter;
}

forcing.ForEach([&](double& f, const double& yn1, const double& yn) { f -= (yn1 - yn) / H; }, Yn1, Yn);

// the result of the linear solver will be stored in forcing
// this represents the change in the solution
linear_solver_.Solve(forcing, state.lower_matrix_, state.upper_matrix_);
Expand All @@ -137,31 +118,14 @@ namespace micm
// solution_blk in camchem
// Yn1 = Yn1 + residual;
// always make sure the solution is positive regardless of which iteration we are on
forcing_iter = forcing.begin();
yn1_iter = Yn1.begin();
for (; forcing_iter != forcing.end(); ++forcing_iter, ++yn1_iter)
{
*yn1_iter = std::max(0.0, *yn1_iter + *forcing_iter);
}
Yn1.ForEach([&](double& yn1, const double& f) { yn1 = std::max( 0.0, yn1 + f ); }, forcing);

// if this is the first iteration, we don't need to check for convergence
if (iterations++ == 0)
continue;

// check for convergence
forcing_iter = forcing.begin();
yn1_iter = Yn1.begin();

// convergence happens when the absolute value of the change to the solution
// is less than a tolerance times the absolute value of the solution
auto abs_tol_iter = parameters_.absolute_tolerance_.begin();
do
{
// changes that are much smaller than the tolerance are negligible and we assume can be accepted
converged = (std::abs(*forcing_iter) <= small) || (std::abs(*forcing_iter) <= *abs_tol_iter) ||
(std::abs(*forcing_iter) <= parameters_.relative_tolerance_ * std::abs(*yn1_iter));
++forcing_iter, ++yn1_iter, ++abs_tol_iter;
} while (converged && forcing_iter != forcing.end());
converged = IsConverged(parameters_, forcing, Yn1);
} while (!converged && iterations < max_iter);

if (!converged)
Expand Down Expand Up @@ -203,4 +167,74 @@ namespace micm
result.final_time_ = t;
return result;
}

template<class RatesPolicy, class LinearSolverPolicy>
template<class DenseMatrixPolicy>
inline bool BackwardEuler<RatesPolicy, LinearSolverPolicy>::IsConverged(const BackwardEulerSolverParameters& parameters, const DenseMatrixPolicy& residual, const DenseMatrixPolicy& state)
requires(!VectorizableDense<DenseMatrixPolicy>)
{
double small = parameters.small_;
double rel_tol = parameters.relative_tolerance_;
auto& abs_tol = parameters.absolute_tolerance_;
auto residual_iter = residual.AsVector().begin();
auto state_iter = state.AsVector().begin();
const std::size_t n_elem = residual.NumRows() * residual.NumColumns();
const std::size_t n_vars = abs_tol.size();
for (std::size_t i = 0; i < n_elem; ++i)
{
if (std::abs(*residual_iter) > small && std::abs(*residual_iter) > abs_tol[i % n_vars] &&
std::abs(*residual_iter) > rel_tol * std::abs(*state_iter))
{
return false;
}
++residual_iter, ++state_iter;
}
return true;
}

template<class RatesPolicy, class LinearSolverPolicy>
template<class DenseMatrixPolicy>
inline bool BackwardEuler<RatesPolicy, LinearSolverPolicy>::IsConverged(const BackwardEulerSolverParameters& parameters, const DenseMatrixPolicy& residual, const DenseMatrixPolicy& state)
requires(VectorizableDense<DenseMatrixPolicy>)
{
double small = parameters.small_;
double rel_tol = parameters.relative_tolerance_;
auto& abs_tol = parameters.absolute_tolerance_;
auto residual_iter = residual.AsVector().begin();
auto state_iter = state.AsVector().begin();
const std::size_t n_elem = residual.NumRows() * residual.NumColumns();
const std::size_t L = residual.GroupVectorSize();
const std::size_t n_vars = abs_tol.size();
const std::size_t whole_blocks = std::floor(residual.NumRows() / L) * residual.GroupSize();

// evaluate the rows that fit exactly into the vectorizable dimension (L)
for (std::size_t i = 0; i < whole_blocks; ++i)
{
if (std::abs(*residual_iter) > small && std::abs(*residual_iter) > abs_tol[(i / L) % n_vars] &&
std::abs(*residual_iter) > rel_tol * std::abs(*state_iter))
{
return false;
}
++residual_iter, ++state_iter;
}

// evaluate the remaining rows
const std::size_t remaining_rows = residual.NumRows() % L;
if (remaining_rows > 0)
{
for (std::size_t y = 0; y < residual.NumColumns(); ++y)
{
const std::size_t offset = y * L;
for (std::size_t i = offset; i < offset + remaining_rows; ++i)
{
if (std::abs(residual_iter[i]) > small && std::abs(residual_iter[i]) > abs_tol[y] &&
std::abs(residual_iter[i]) > rel_tol * std::abs(state_iter[i]))
{
return false;
}
}
}
}
return true;
}
} // namespace micm
6 changes: 4 additions & 2 deletions include/micm/solver/backward_euler_solver_parameters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ namespace micm

std::vector<double> absolute_tolerance_;
double relative_tolerance_{ 1.0e-8 };
double small{ 1.0e-40 };
double small_{ 1.0e-40 };
size_t max_number_of_steps_{ 11 };
std::array<double, 5> time_step_reductions{ 0.5, 0.5, 0.5, 0.5, 0.1 };
// The time step reductions are used to determine the time step after a failed solve
// This default set of time step reductions is used by CAM-Chem
std::array<double, 5> time_step_reductions_{ 0.5, 0.5, 0.5, 0.5, 0.1 };
};

} // namespace micm
4 changes: 2 additions & 2 deletions include/micm/solver/linear_solver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ namespace micm
const std::function<LuDecompositionPolicy(const SparseMatrixPolicy&)> create_lu_decomp);

/// @brief Decompose the matrix into upper and lower triangular matrices
void Factor(const SparseMatrixPolicy& matrix, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix);
void Factor(const SparseMatrixPolicy& matrix, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix) const;

/// @brief Decompose the matrix into upper and lower triangular matrices
/// @param matrix Matrix to decompose into lower and upper triangular matrices
Expand All @@ -84,7 +84,7 @@ namespace micm
const SparseMatrixPolicy& matrix,
SparseMatrixPolicy& lower_matrix,
SparseMatrixPolicy& upper_matrix,
bool& is_singular);
bool& is_singular) const;

/// @brief Solve for x in Ax = b. x should be a copy of b and after Solve finishes x will contain the result
template<class MatrixPolicy>
Expand Down
4 changes: 2 additions & 2 deletions include/micm/solver/linear_solver.inl
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ namespace micm
inline void LinearSolver<SparseMatrixPolicy, LuDecompositionPolicy>::Factor(
const SparseMatrixPolicy& matrix,
SparseMatrixPolicy& lower_matrix,
SparseMatrixPolicy& upper_matrix)
SparseMatrixPolicy& upper_matrix) const
{
MICM_PROFILE_FUNCTION();

Expand All @@ -123,7 +123,7 @@ namespace micm
const SparseMatrixPolicy& matrix,
SparseMatrixPolicy& lower_matrix,
SparseMatrixPolicy& upper_matrix,
bool& is_singular)
bool& is_singular) const
{
MICM_PROFILE_FUNCTION();

Expand Down
12 changes: 9 additions & 3 deletions include/micm/util/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once

static constexpr double BOLTZMANN_CONSTANT = 1.380649e-23; // J K^{-1}
static constexpr double AVOGADRO_CONSTANT = 6.02214076e23; // # mol^{-1}
static constexpr double GAS_CONSTANT = BOLTZMANN_CONSTANT * AVOGADRO_CONSTANT; // J K^{-1} mol^{-1}
namespace micm
{
namespace constants
{
static constexpr double BOLTZMANN_CONSTANT = 1.380649e-23; // J K^{-1}
static constexpr double AVOGADRO_CONSTANT = 6.02214076e23; // # mol^{-1}
static constexpr double GAS_CONSTANT = BOLTZMANN_CONSTANT * AVOGADRO_CONSTANT; // J K^{-1} mol^{-1}
} // namespace constants
} // namespace micm
10 changes: 8 additions & 2 deletions include/micm/util/property_keys.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@

#include <string>

static const std::string GAS_DIFFUSION_COEFFICIENT = "diffusion coefficient [m2 s-1]";
static const std::string MOLECULAR_WEIGHT = "molecular weight [kg mol-1]";
namespace micm
{
namespace keys
{
static const std::string GAS_DIFFUSION_COEFFICIENT = "diffusion coefficient [m2 s-1]";
static const std::string MOLECULAR_WEIGHT = "molecular weight [kg mol-1]";
boulderdaze marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading
Loading