Skip to content

Commit a48b28f

Browse files
Merge pull request #2677 from pybamm-team/i2643-banded
I2643 banded
2 parents 8521bc9 + 9cfd35b commit a48b28f

File tree

12 files changed

+128
-23
lines changed

12 files changed

+128
-23
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
## Features
44

5-
- Added temperature control to experiment class. ([#2518])(https://github.com/pybamm-team/PyBaMM/pull/2518)
5+
- Added an option for using a banded jacobian and sundials banded solvers for the IDAKLU solve [#2677](https://github.com/pybamm-team/PyBaMM/pull/2677)
66
- The "particle size" option can now be a tuple to allow different behaviour in each electrode([#2672](https://github.com/pybamm-team/PyBaMM/pull/2672)).
7+
- Added temperature control to experiment class. [#2518](https://github.com/pybamm-team/PyBaMM/pull/2518)
78

89
## Bug fixes
910

pybamm/solvers/c_solvers/idaklu.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ PYBIND11_MODULE(idaklu, m)
4444
py::arg("number_of_parameters"), py::arg("rhs_alg"),
4545
py::arg("jac_times_cjmass"), py::arg("jac_times_cjmass_colptrs"),
4646
py::arg("jac_times_cjmass_rowvals"), py::arg("jac_times_cjmass_nnz"),
47+
py::arg("jac_bandwidth_lower"), py::arg("jac_bandwidth_upper"),
4748
py::arg("jac_action"), py::arg("mass_action"), py::arg("sens"),
4849
py::arg("events"), py::arg("number_of_events"), py::arg("rhs_alg_id"),
4950
py::arg("atol"), py::arg("rtol"), py::arg("inputs"), py::arg("options"),

pybamm/solvers/c_solvers/idaklu/casadi_functions.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,20 @@ void CasadiFunction::operator()()
3434
CasadiFunctions::CasadiFunctions(
3535
const Function &rhs_alg, const Function &jac_times_cjmass,
3636
const int jac_times_cjmass_nnz,
37+
const int jac_bandwidth_lower, const int jac_bandwidth_upper,
3738
const np_array_int &jac_times_cjmass_rowvals_arg,
3839
const np_array_int &jac_times_cjmass_colptrs_arg,
3940
const int inputs_length, const Function &jac_action,
4041
const Function &mass_action, const Function &sens, const Function &events,
4142
const int n_s, int n_e, const int n_p, const Options& options)
4243
: number_of_states(n_s), number_of_events(n_e), number_of_parameters(n_p),
43-
number_of_nnz(jac_times_cjmass_nnz), rhs_alg(rhs_alg),
44+
number_of_nnz(jac_times_cjmass_nnz),
45+
jac_bandwidth_lower(jac_bandwidth_lower), jac_bandwidth_upper(jac_bandwidth_upper),
46+
rhs_alg(rhs_alg),
4447
jac_times_cjmass(jac_times_cjmass), jac_action(jac_action),
4548
mass_action(mass_action), sens(sens), events(events),
46-
tmp(number_of_states),
49+
tmp_state_vector(number_of_states),
50+
tmp_sparse_jacobian_data(jac_times_cjmass_nnz),
4751
options(options)
4852
{
4953

@@ -66,4 +70,5 @@ CasadiFunctions::CasadiFunctions(
6670

6771
}
6872

69-
realtype *CasadiFunctions::get_tmp() { return tmp.data(); }
73+
realtype *CasadiFunctions::get_tmp_state_vector() { return tmp_state_vector.data(); }
74+
realtype *CasadiFunctions::get_tmp_sparse_jacobian_data() { return tmp_sparse_jacobian_data.data(); }

pybamm/solvers/c_solvers/idaklu/casadi_functions.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ class CasadiFunctions
3131
int number_of_parameters;
3232
int number_of_events;
3333
int number_of_nnz;
34+
int jac_bandwidth_lower;
35+
int jac_bandwidth_upper;
3436
CasadiFunction rhs_alg;
3537
CasadiFunction sens;
3638
CasadiFunction jac_times_cjmass;
@@ -44,17 +46,20 @@ class CasadiFunctions
4446

4547
CasadiFunctions(const Function &rhs_alg, const Function &jac_times_cjmass,
4648
const int jac_times_cjmass_nnz,
49+
const int jac_bandwidth_lower, const int jac_bandwidth_upper,
4750
const np_array_int &jac_times_cjmass_rowvals,
4851
const np_array_int &jac_times_cjmass_colptrs,
4952
const int inputs_length, const Function &jac_action,
5053
const Function &mass_action, const Function &sens,
5154
const Function &events, const int n_s, int n_e,
5255
const int n_p, const Options& options);
5356

54-
realtype *get_tmp();
57+
realtype *get_tmp_state_vector();
58+
realtype *get_tmp_sparse_jacobian_data();
5559

5660
private:
57-
std::vector<realtype> tmp;
61+
std::vector<realtype> tmp_state_vector;
62+
std::vector<realtype> tmp_sparse_jacobian_data;
5863
};
5964

6065
#endif // PYBAMM_IDAKLU_CASADI_FUNCTIONS_HPP

pybamm/solvers/c_solvers/idaklu/casadi_solver.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,31 @@ create_casadi_solver(int number_of_states, int number_of_parameters,
88
const Function &rhs_alg, const Function &jac_times_cjmass,
99
const np_array_int &jac_times_cjmass_colptrs,
1010
const np_array_int &jac_times_cjmass_rowvals,
11-
const int jac_times_cjmass_nnz, const Function &jac_action,
11+
const int jac_times_cjmass_nnz,
12+
const int jac_bandwidth_lower, const int jac_bandwidth_upper,
13+
const Function &jac_action,
1214
const Function &mass_action, const Function &sens,
1315
const Function &events, const int number_of_events,
1416
np_array rhs_alg_id, np_array atol_np, double rel_tol,
1517
int inputs_length, py::dict options)
1618
{
1719
auto options_cpp = Options(options);
1820
auto functions = std::make_unique<CasadiFunctions>(
19-
rhs_alg, jac_times_cjmass, jac_times_cjmass_nnz, jac_times_cjmass_rowvals,
21+
rhs_alg, jac_times_cjmass, jac_times_cjmass_nnz, jac_bandwidth_lower, jac_bandwidth_upper, jac_times_cjmass_rowvals,
2022
jac_times_cjmass_colptrs, inputs_length, jac_action, mass_action, sens,
2123
events, number_of_states, number_of_events, number_of_parameters,
2224
options_cpp);
2325

2426
return new CasadiSolver(atol_np, rel_tol, rhs_alg_id, number_of_parameters,
25-
number_of_events, jac_times_cjmass_nnz,
27+
number_of_events, jac_times_cjmass_nnz,
28+
jac_bandwidth_lower, jac_bandwidth_upper,
2629
std::move(functions), options_cpp);
2730
}
2831

2932
CasadiSolver::CasadiSolver(np_array atol_np, double rel_tol,
3033
np_array rhs_alg_id, int number_of_parameters,
3134
int number_of_events, int jac_times_cjmass_nnz,
35+
int jac_bandwidth_lower, int jac_bandwidth_upper,
3236
std::unique_ptr<CasadiFunctions> functions_arg,
3337
const Options &options)
3438
: number_of_states(atol_np.request().size),
@@ -107,7 +111,14 @@ CasadiSolver::CasadiSolver(np_array atol_np, double rel_tol,
107111
jac_times_cjmass_nnz, CSC_MAT);
108112
#endif
109113
}
110-
else if (options.jacobian == "dense" || options.jacobian == "none")
114+
else if (options.jacobian == "banded") {
115+
DEBUG("\tsetting banded matrix");
116+
#if SUNDIALS_VERSION_MAJOR >= 6
117+
J = SUNBandMatrix(number_of_states, jac_bandwidth_upper, jac_bandwidth_lower, sunctx);
118+
#else
119+
J = SUNBandMatrix(number_of_states, jac_bandwidth_upper, jac_bandwidth_lower);
120+
#endif
121+
} else if (options.jacobian == "dense" || options.jacobian == "none")
111122
{
112123
DEBUG("\tsetting dense matrix");
113124
#if SUNDIALS_VERSION_MAJOR >= 6
@@ -151,6 +162,15 @@ CasadiSolver::CasadiSolver(np_array atol_np, double rel_tol,
151162
LS = SUNLinSol_KLU(yy, J, sunctx);
152163
#else
153164
LS = SUNLinSol_KLU(yy, J);
165+
#endif
166+
}
167+
else if (options.linear_solver == "SUNLinSol_Band")
168+
{
169+
DEBUG("\tsetting SUNLinSol_Band linear solver");
170+
#if SUNDIALS_VERSION_MAJOR >= 6
171+
LS = SUNLinSol_Band(yy, J, sunctx);
172+
#else
173+
LS = SUNLinSol_Band(yy, J);
154174
#endif
155175
}
156176
else if (options.linear_solver == "SUNLinSol_SPBCGS")

pybamm/solvers/c_solvers/idaklu/casadi_solver.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class CasadiSolver
1414
public:
1515
CasadiSolver(np_array atol_np, double rel_tol, np_array rhs_alg_id,
1616
int number_of_parameters, int number_of_events,
17-
int jac_times_cjmass_nnz,
17+
int jac_times_cjmass_nnz, int jac_bandwidth_lower, int jac_bandwidth_upper,
1818
std::unique_ptr<CasadiFunctions> functions, const Options& options);
1919
~CasadiSolver();
2020

@@ -48,7 +48,9 @@ create_casadi_solver(int number_of_states, int number_of_parameters,
4848
const Function &rhs_alg, const Function &jac_times_cjmass,
4949
const np_array_int &jac_times_cjmass_colptrs,
5050
const np_array_int &jac_times_cjmass_rowvals,
51-
const int jac_times_cjmass_nnz, const Function &jac_action,
51+
const int jac_times_cjmass_nnz,
52+
const int jac_bandwidth_lower, const int jac_bandwidth_upper,
53+
const Function &jac_action,
5254
const Function &mass_action, const Function &sens,
5355
const Function &event, const int number_of_events,
5456
np_array rhs_alg_id, np_array atol_np,

pybamm/solvers/c_solvers/idaklu/casadi_sundials_functions.cpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ int residual_casadi(realtype tres, N_Vector yy, N_Vector yp, N_Vector rr,
1515
p_python_functions->rhs_alg.m_res[0] = NV_DATA_S(rr);
1616
p_python_functions->rhs_alg();
1717

18-
realtype *tmp = p_python_functions->get_tmp();
18+
realtype *tmp = p_python_functions->get_tmp_state_vector();
1919
p_python_functions->mass_action.m_arg[0] = NV_DATA_S(yp);
2020
p_python_functions->mass_action.m_res[0] = tmp;
2121
p_python_functions->mass_action();
@@ -108,7 +108,7 @@ int jtimes_casadi(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr,
108108
p_python_functions->jac_action();
109109

110110
// tmp has -∂F/∂y˙ v
111-
realtype *tmp = p_python_functions->get_tmp();
111+
realtype *tmp = p_python_functions->get_tmp_state_vector();
112112
p_python_functions->mass_action.m_arg[0] = NV_DATA_S(v);
113113
p_python_functions->mass_action.m_res[0] = tmp;
114114
p_python_functions->mass_action();
@@ -148,15 +148,14 @@ int jacobian_casadi(realtype tt, realtype cj, N_Vector yy, N_Vector yp,
148148
static_cast<CasadiFunctions *>(user_data);
149149

150150
// create pointer to jac data, column pointers, and row values
151-
sunindextype *jac_colptrs;
152-
sunindextype *jac_rowvals;
153151
realtype *jac_data;
154152
if (p_python_functions->options.using_sparse_matrix)
155153
{
156-
jac_colptrs = SUNSparseMatrix_IndexPointers(JJ);
157-
jac_rowvals = SUNSparseMatrix_IndexValues(JJ);
158154
jac_data = SUNSparseMatrix_Data(JJ);
159155
}
156+
else if (p_python_functions->options.using_banded_matrix) {
157+
jac_data = p_python_functions->get_tmp_sparse_jacobian_data();
158+
}
160159
else
161160
{
162161
jac_data = SUNDenseMatrix_Data(JJ);
@@ -169,10 +168,31 @@ int jacobian_casadi(realtype tt, realtype cj, N_Vector yy, N_Vector yp,
169168
p_python_functions->inputs.data();
170169
p_python_functions->jac_times_cjmass.m_arg[3] = &cj;
171170
p_python_functions->jac_times_cjmass.m_res[0] = jac_data;
171+
172172
p_python_functions->jac_times_cjmass();
173173

174-
if (p_python_functions->options.using_sparse_matrix)
174+
175+
if (p_python_functions->options.using_banded_matrix)
175176
{
177+
// copy data from temporary matrix to the banded matrix
178+
auto jac_colptrs = p_python_functions->jac_times_cjmass_colptrs.data();
179+
auto jac_rowvals = p_python_functions->jac_times_cjmass_rowvals.data();
180+
int ncols = p_python_functions->number_of_states;
181+
for (int col_ij = 0; col_ij < ncols; col_ij++) {
182+
realtype *banded_col = SM_COLUMN_B(JJ, col_ij);
183+
for (auto data_i = jac_colptrs[col_ij]; data_i < jac_colptrs[col_ij+1]; data_i++) {
184+
auto row_ij = jac_rowvals[data_i];
185+
const realtype value_ij = jac_data[data_i];
186+
DEBUG("(" << row_ij << ", " << col_ij << ") = " << value_ij);
187+
SM_COLUMN_ELEMENT_B(banded_col, row_ij, col_ij) = value_ij;
188+
}
189+
}
190+
}
191+
else if (p_python_functions->options.using_sparse_matrix)
192+
{
193+
194+
sunindextype *jac_colptrs = SUNSparseMatrix_IndexPointers(JJ);
195+
sunindextype *jac_rowvals = SUNSparseMatrix_IndexValues(JJ);
176196
// row vals and col ptrs
177197
const int n_row_vals = p_python_functions->jac_times_cjmass_rowvals.size();
178198
auto p_jac_times_cjmass_rowvals =
@@ -262,7 +282,7 @@ int sensitivities_casadi(int Ns, realtype t, N_Vector yy, N_Vector yp,
262282
for (int i = 0; i < np; i++)
263283
{
264284
// put (∂F/∂y)s i (t) in tmp
265-
realtype *tmp = p_python_functions->get_tmp();
285+
realtype *tmp = p_python_functions->get_tmp_state_vector();
266286
p_python_functions->jac_action.m_arg[0] = &t;
267287
p_python_functions->jac_action.m_arg[1] = NV_DATA_S(yy);
268288
p_python_functions->jac_action.m_arg[2] = p_python_functions->inputs.data();

pybamm/solvers/c_solvers/idaklu/common.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include <sunlinsol/sunlinsol_klu.h> /* access to KLU linear solver */
1818
#include <sunlinsol/sunlinsol_dense.h> /* access to dense linear solver */
19+
#include <sunlinsol/sunlinsol_band.h> /* access to dense linear solver */
1920
#include <sunlinsol/sunlinsol_spbcgs.h> /* access to spbcgs iterative linear solver */
2021
#include <sunlinsol/sunlinsol_spfgmr.h>
2122
#include <sunlinsol/sunlinsol_spgmr.h>

pybamm/solvers/c_solvers/idaklu/options.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "options.hpp"
2+
#include <iostream>
23
#include <stdexcept>
34

45

@@ -15,9 +16,14 @@ Options::Options(py::dict options)
1516
{
1617

1718
using_sparse_matrix = true;
19+
using_banded_matrix = false;
1820
if (jacobian == "sparse")
1921
{
2022
}
23+
else if (jacobian == "banded") {
24+
using_banded_matrix = true;
25+
using_sparse_matrix = false;
26+
}
2127
else if (jacobian == "dense" || jacobian == "none")
2228
{
2329
using_sparse_matrix = false;
@@ -29,7 +35,7 @@ Options::Options(py::dict options)
2935
{
3036
throw std::domain_error(
3137
"Unknown jacobian type \""s + jacobian +
32-
"\". Should be one of \"sparse\", \"dense\", \"matrix-free\" or \"none\"."s
38+
"\". Should be one of \"sparse\", \"banded\", \"dense\", \"matrix-free\" or \"none\"."s
3339
);
3440
}
3541

@@ -40,6 +46,17 @@ Options::Options(py::dict options)
4046
else if (linear_solver == "SUNLinSol_KLU" && jacobian == "sparse")
4147
{
4248
}
49+
else if (linear_solver == "SUNLinSol_Band" && jacobian == "banded")
50+
{
51+
}
52+
else if (jacobian == "banded") {
53+
throw std::domain_error(
54+
"Unknown linear solver or incompatible options: "
55+
"jacobian = \"" + jacobian + "\" linear solver = \"" + linear_solver +
56+
"\". For a banded jacobian "
57+
"please use the SUNLinSol_Band linear solver"
58+
);
59+
}
4360
else if ((linear_solver == "SUNLinSol_SPBCGS" ||
4461
linear_solver == "SUNLinSol_SPFGMR" ||
4562
linear_solver == "SUNLinSol_SPGMR" ||

pybamm/solvers/c_solvers/idaklu/options.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
struct Options {
77
bool print_stats;
88
bool using_sparse_matrix;
9+
bool using_banded_matrix;
910
bool using_iterative_solver;
1011
std::string jacobian;
1112
std::string linear_solver; // klu, lapack, spbcg

0 commit comments

Comments
 (0)