Skip to content

Add Implicit Trapezoidal method - #588

Open
Steven-Roberts wants to merge 13 commits into
developfrom
steven/implicit-trapezoidal
Open

Steven-Roberts wants to merge 13 commits into
developfrom
steven/implicit-trapezoidal

Conversation

@Steven-Roberts

@Steven-Roberts Steven-Roberts commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Adds a fixed step implicit trapezoidal method for comparing against adaptive and partitioned integrators. It uses Newton's method with line search for solving nonlinear systems which was found to be key at large time steps.

Proposed changes

  • Adds new VectorHandler operations for norms
  • Removes internal state management from InfNorm which had an initialization bug and instead use new VectorHandler operations
  • Adds ImplicitTrapezoidal class

Checklist

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

Further comments

Currently, this is not used outside tests. In a separate PR, we'll need to add support for swapping between IDA, Rosenbrock, and this for phasor dynamics application and other "drivers".

@lukelowry

Copy link
Copy Markdown
Collaborator

Will be invaluable for validating EMT. Important!

Comment thread GridKit/Solver/Dynamic/Native/InfNorm.cpp Outdated
Comment thread GridKit/LinearAlgebra/Vector/VectorHandlerCpu.cpp Outdated
Comment thread GridKit/LinearAlgebra/Vector/VectorHandlerCpu.cpp
Comment thread GridKit/Solver/Dynamic/Native/RmsNorm.cpp Outdated
Comment on lines +17 to +26
#define BUBBLE_STEP_FAIL(arg) \
do \
{ \
if (int err = (arg)) \
{ \
jacobian_valid_ = false; \
(void) restoreAcceptedState(); \
return err; \
} \
} while (false)

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.

instead of this scope-guard looking thing, why can't we use something like the experimental scope guards? there are similar things implemented in third-party libraries which could also be used.

Comment on lines +8 to +15
#define BUBBLE_FAIL(arg) \
do \
{ \
if (int err = (arg)) \
{ \
return err; \
} \
} while (false)

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.

this seems indicative of an argument for more use of exceptions to avoid this sort of code being used all over the place. not very relevant to this pr, though, outside of wanting to remove these file-local error handling helpers.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I followed the convention in Rosenbrock.cpp, but I agree exceptions would be more idiomatic than int return values and macros like this.

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.

@alexander-novo, could we think of a better way to handle errors?

@Steven-Roberts, how are similar instances handled in SUNDIALS?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@Steven-Roberts, how are similar instances handled in SUNDIALS?

With a similar macro or just writing out the if statement to check the return value. But SUNDIALS does not have access to exceptions, std::expected, and other C++ error handling mechanisms.

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.

@alexander-novo, could we think of a better way to handle errors?

The returning an int for an error code is a very C-like way of handling errors, and using these kind of quality-of-life macros isn't so uncommon in C I think.

A more C++-like approach might be more natural. std::expected or std::variant as suggested by @superwhiskers work very well in Rust, but won't help us with this specific boilerplate issue. Throwing exceptions will reduce the boilerplate heavily, but we'll need to make sure to heavily document which kinds of exceptions can be thrown by functions.

@Steven-Roberts
Steven-Roberts force-pushed the steven/implicit-trapezoidal branch from 39c0ad2 to 9084820 Compare October 6, 2026 18:02
@Steven-Roberts
Steven-Roberts force-pushed the steven/implicit-trapezoidal branch from dc6388d to 646b46c Compare October 13, 2026 01:44

@pelesh pelesh left a comment

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 would suggest separating the interface to the ImplicitTrapezoidal integrator from the integrator itself. This is on to-do list for the Rosenbrock, as well.

For example Ida class is just an interface to IDA solver. Imho ImplicitTrapezoidal interface should follow the same API and the integrator implementation should be within a directory GridKit/Solver/Dynamics/ImplicitTrapezoidal.

Comment on lines +306 to +307
throwOnError(timeStep(current_time_, parameters.step_size_, parameters));
if (step_callback)

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.

Is this really the intent here to throw an exception upon failed step? Why not simply clean-up and return with error message? I think exception should be reserved for events that should not happen. For a fixed step solver it is not unexpected that for certain step selection the computation would not converge.

Comment on lines +284 to +285
const std::vector<RealT>& output_times,
Parameters parameters,

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 would avoid Matlab approach of storing output times a priori. I would especially avoid using std::vector in this code.

Comment on lines +286 to +287
std::optional<std::function<void(RealT)>> output_callback,
std::optional<std::function<void(const StepInfo&)>> step_callback)

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 would avoid these callbacks. We are trying to remove them altogether from the code. I don't think they should be in the core numerical integrator code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants