Skip to content

Refactor Jacobian assembly#339

Draft
KakeruUeda wants to merge 8 commits intodevelopfrom
kakeru/csr-jac
Draft

Refactor Jacobian assembly#339
KakeruUeda wants to merge 8 commits intodevelopfrom
kakeru/csr-jac

Conversation

@KakeruUeda
Copy link
Collaborator

@KakeruUeda KakeruUeda commented Feb 13, 2026

Description

Jacobian assembly currently uses a COO matrix, and we sort and rebuild CSR row pointers each step for IDA. The overhead from this process can become a bottleneck as nnz grows. This change keeps the system Jacobian in CSR and preserves a COO→CSR mapping for direct CSR updates.

Closes #301

cc @nkoukpaizan

Proposed changes

  • Store the system Jacobian in CSR format in PowerElectronicsModel.
  • Update allocate() to populate CSR entries and preserve the mapping.
  • Add CsrJac for the solver configuration (PhasorDynamics still uses the Jac function).

Currently PowerElectronics only.

Checklist

  • All tests pass.
  • Code compiles cleanly with flags -Wall -Wpedantic -Wconversion -Wextra.
  • The new code follows GridKit™ style guidelines.
  • [N/A] There are unit tests for the new code.
  • The new code is documented.
  • [N/A] The feature branch is rebased with respect to the target branch.
  • I have updated CHANGELOG.md to reflect the changes in this PR. If this is a minor PR that is part of a larger fix already included in the file, state so.

@KakeruUeda KakeruUeda self-assigned this Feb 14, 2026
@KakeruUeda
Copy link
Collaborator Author

KakeruUeda commented Feb 16, 2026

Timed runSimulation(t_final) in the microgrid app. CSR assembly improved runtime, while the error remained unchanged.

Before
Screenshot 2026-02-16 083255

After
Screenshot 2026-02-16 083351

Copy link
Collaborator

@shakedregev shakedregev left a comment

Choose a reason for hiding this comment

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

Still need to test this. Looks good except for naming inconsistencies.

IdxT rows_size_;
IdxT columns_size_;
bool sorted_;
std::vector<IdxT> map2csr_;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Change this to follow naming conventions globally.
Use _ and to instead of 2

const IdxT nnz_dup = static_cast<IdxT>(row_indices_.size());

// Sort the entries while preserving the mapping
std::vector<IdxT> map2sorted(nnz_dup);
Copy link
Collaborator

Choose a reason for hiding this comment

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

see above


// Deduplicate entries by summing values
std::vector<IdxT> row_ptrs(rows_size_ + 1, 0);
std::vector<IdxT> map2dedup(nnz_dup);
Copy link
Collaborator

Choose a reason for hiding this comment

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

see above

for (size_t i = 0; i < ordervec.size(); i++)
{
// permutation swap
while (ordervec[i] != ordervec[ordervec[i]])
Copy link
Collaborator

Choose a reason for hiding this comment

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

underscores

Comment on lines 837 to 867
int Ida<ScalarT, IdxT>::CsrJac(RealT t, RealT cj, N_Vector yy, N_Vector yp, N_Vector, SUNMatrix J, void* user_data, N_Vector, N_Vector, N_Vector)
{
GridKit::Model::Evaluator<ScalarT, IdxT>* model = static_cast<GridKit::Model::Evaluator<ScalarT, IdxT>*>(user_data);

model->updateTime(t, cj);
copyVec(yy, model->y());
copyVec(yp, model->yp());

model->evaluateJacobian();

using CsrMatrix = GridKit::LinearAlgebra::CsrMatrix<RealT, IdxT>;
const CsrMatrix* Jac = model->getCsrJacobian();

SUNMatZero(J);

sunindextype* sun_rptr = SUNSparseMatrix_IndexPointers(J);
sunindextype* sun_cind = SUNSparseMatrix_IndexValues(J);
RealT* sun_vals = SUNSparseMatrix_Data(J);

IdxT n = Jac->getRows();
IdxT nnz = Jac->getNnz();

// Get reference to the jacobian entries
const IdxT* rptr = Jac->getRowPtr();
const IdxT* cind = Jac->getColInd();
const RealT* vals = Jac->getValues();

// Copy data from model jac to sundials
std::copy(rptr, rptr + n + 1, sun_rptr);
std::copy(cind, cind + nnz, sun_cind);
std::copy(vals, vals + nnz, sun_vals);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume these are copied over, but this file violates the naming conventions in almost every variable and function.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Some of these are SUNDIALS functions, so they don't follow our style guidelines.

Copy link
Collaborator

@shakedregev shakedregev left a comment

Choose a reason for hiding this comment

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

Approved pending fixes to variable names

@shakedregev
Copy link
Collaborator

Timed runSimulation(t_final) in the microgrid app. CSR assembly improved runtime, while the error remained unchanged.

Before Screenshot 2026-02-16 083255

After Screenshot 2026-02-16 083351

@shakedregev shakedregev reopened this Feb 16, 2026
@shakedregev
Copy link
Collaborator

It's impressive that there are such substantial speed gains on even a small system. This means that for real systems it's probably astronomical. I wonder if we can test that.

Comment on lines +129 to +144
// Basic Verification test
std::vector<size_t> csr_rtest = {0, 2, 4, 6};
std::vector<size_t> csr_ctest = {0, 1, 1, 2, 0, 2};
std::vector<double> csr_valtest = {2.6, 1.1, 2.9, 1.3, 1.4, 3.3};
std::vector<size_t> maptest = {2, 5, 0, 0, 4, 2, 1, 5, 3};

std::vector<size_t>& map_to_csr = C.getMapToCsr();

assert(csr_rtest.size() == csr_r.size());
assert(csr_ctest.size() == csr_c.size());
assert(csr_valtest.size() == csr_v.size());
assert(maptest.size() == map_to_csr.size());

for (size_t i = 0; i < csr_rtest.size(); i++)
{
if (csr_r[i] != csr_rtest[i])
Copy link
Collaborator

Choose a reason for hiding this comment

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

_ before test

Comment on lines +89 to +96
std::vector<IdxT>& getMapToCsr();

void printMatrix(std::string name = "");

void printMatrixMarket(const std::string& filename, const std::string& comment);

static void sortSparseCOO(std::vector<IdxT>& rows, std::vector<IdxT>& columns, std::vector<RealT>& values);
static void sortSparseCOO(std::vector<IdxT>& rows, std::vector<IdxT>& columns, std::vector<RealT>& values, std::vector<IdxT>& map);
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should consistently capitalize entire acronyms or just the first letter. I tend to favor the Csr vs COO style better. We are essentially using the acronym as a word. In comments, we can have it all capitalized.

Copy link
Collaborator

Choose a reason for hiding this comment

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

PS: Looks like most of the code actually capitalizes the entire acronym for COO, but not for CSR. @pelesh - thoughts?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree on this

Copy link
Collaborator

@pelesh pelesh Feb 16, 2026

Choose a reason for hiding this comment

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

Please follow style guidelines. Some of the non-compliant code was grandfathered in. That is why we still have some inconsistencies.

Comment on lines +967 to +969
std::vector<size_t> order_vec(rows.size());
std::size_t n(0);
std::generate(std::begin(order_vec), std::end(order_vec), [&]
Copy link
Collaborator

@shakedregev shakedregev Feb 16, 2026

Choose a reason for hiding this comment

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

I missed this (perhaps multiple times), but why are we using std::vector here? Is this to keep consistency with the rest of the code?
Edit: is this just for comparison?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, to keep consistency with the rest of the code for now.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove std::vector here.

Copy link
Collaborator

@pelesh pelesh left a comment

Choose a reason for hiding this comment

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

  • CSR matrix implementation should remain in the *.cpp file.
  • I would avoid using STL containers here because we want to prototype parallel implementations.
  • Do not use existing COO matrix implementation in GridKit. If needed, port COO matrix from Re::Solve.

I made some other (mostly nitpicking) comments about the code.

Comment on lines +236 to +237
template <typename RealT, typename IdxT>
std::tuple<std::vector<IdxT>, std::vector<IdxT>, std::vector<RealT>> COO_Matrix<RealT, IdxT>::getCsrData()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do not use std::tuple.

Comment on lines +89 to +96
std::vector<IdxT>& getMapToCsr();

void printMatrix(std::string name = "");

void printMatrixMarket(const std::string& filename, const std::string& comment);

static void sortSparseCOO(std::vector<IdxT>& rows, std::vector<IdxT>& columns, std::vector<RealT>& values);
static void sortSparseCOO(std::vector<IdxT>& rows, std::vector<IdxT>& columns, std::vector<RealT>& values, std::vector<IdxT>& map);
Copy link
Collaborator

@pelesh pelesh Feb 16, 2026

Choose a reason for hiding this comment

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

Please follow style guidelines. Some of the non-compliant code was grandfathered in. That is why we still have some inconsistencies.

Comment on lines +243 to +244
sortSparseCOO(row_indices_, column_indices_, values_, map_to_sorted);

Copy link
Collaborator

@pelesh pelesh Feb 16, 2026

Choose a reason for hiding this comment

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

Per guidelines:

Suggested change
sortSparseCOO(row_indices_, column_indices_, values_, map_to_sorted);
sortSparseCoo(row_indices_, column_indices_, values_, map_to_sorted);

* @param[in] n number of columns
*/
template <typename RealT, typename IdxT>
void COO_Matrix<RealT, IdxT>::resetEntries(std::vector<IdxT> r, std::vector<IdxT> c, std::vector<RealT> v, IdxT m, IdxT n)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would avoid using COO_Matrix altogether. We are going to remove this class.

Comment on lines +967 to +969
std::vector<size_t> order_vec(rows.size());
std::size_t n(0);
std::generate(std::begin(order_vec), std::end(order_vec), [&]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove std::vector here.

@pelesh pelesh marked this pull request as draft February 16, 2026 23:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement analysis in SystemModelPowerElectronics class to compute system Jacobian sparsity pattern

3 participants