Tempus Version of the Day
Time Integration
Loading...
Searching...
No Matches
Transition from Example 3 to Example 4

Goal

The primary goal of Example 4: Add SolutionHistory is to move from a single Tempus::SolutionState to a Tempus::SolutionHistory while preserving the same Thyra::ModelEvaluator-based model and the same explicit Forward Euler update used in Example 3: Introduce SolutionState.

New concepts introduced are:

  • Tempus::SolutionHistory
  • Tempus::createSolutionHistoryState()
    • creates a solution history initialized with an initial solution state
  • initWorkingState()
    • initializes a working state for the next tentative time step
  • getCurrentState()
    • returns the current accepted solution state
  • getWorkingState()
    • returns the tentative working solution state
  • promoteWorkingState()
    • promotes the accepted working state to become the new current state

This is the first step in moving from state-based solution management to history-based solution management. That transition is important because many time integration algorithms require access to multiple states, including the current solution, tentative working states, and previously accepted states.

What stays the same

  • The van der Pol model is still provided by a Thyra::ModelEvaluator.
  • The timestepper is still Forward Euler.
  • The application still controls the time loop directly.
  • The regression check follows the same pattern.

Selected code excerpts

The code excerpts below highlight the main changes needed to move from a single Tempus::SolutionState to a Tempus::SolutionHistory.


Create the solution history

Create a SolutionHistory. The initial condition is still stored in a Tempus::SolutionState, but that state is now inserted into a Tempus::SolutionHistory.

Before

model->getNominalValues().get_x()->clone_v());
solState->setIndex (0);
solState->setTime (0.0);
solState->setTimeStep(0.0);
solState->setSolutionStatus(Tempus::Status::PASSED);
Teuchos::RCP< SolutionState< Scalar > > createSolutionStateX(const Teuchos::RCP< Thyra::VectorBase< Scalar > > &x, const Teuchos::RCP< Thyra::VectorBase< Scalar > > &xdot=Teuchos::null, const Teuchos::RCP< Thyra::VectorBase< Scalar > > &xdotdot=Teuchos::null)
Nonmember constructor from non-const solution vectors, x.

After

model->getNominalValues().get_x()->clone_v());
solState->setIndex (0);
solState->setTime (0.0);
solState->setTimeStep(0.0);
solState->setSolutionStatus(Tempus::Status::PASSED);
auto solHistory = Tempus::createSolutionHistoryState<double>(solState);

The initial condition remains the same, but it is now managed through a Tempus::SolutionHistory.


Access current and working states

Use SolutionHistory to access states. Instead of working directly with a single solution state, the application now asks the history object for the current and working states.

Before

while (solState->getSolutionStatus() == Tempus::Status::PASSED &&
solState->getTime() < finalTime &&
solState->getIndex() < nTimeSteps) {
RCP<Thyra::VectorBase<double> > x_n = solState->getX();
RCP<Thyra::VectorBase<double> > x_np1 = solState->getX()->clone_v();
solState->setSolutionStatus(Tempus::Status::WORKING);

After

while (solHistory->getCurrentState()->getSolutionStatus() ==
solHistory->getCurrentState()->getTime() < finalTime &&
solHistory->getCurrentState()->getIndex() < nTimeSteps) {
solHistory->initWorkingState();
auto currentState = solHistory->getCurrentState();
auto workingState = solHistory->getWorkingState();
RCP<Thyra::VectorBase<double> > x_n = currentState->getX();
RCP<Thyra::VectorBase<double> > x_np1 = workingState->getX();

The current and tentative states are now obtained through the history object rather than being managed manually.


Update metadata on the working state

Assign metadata to the working state. The timestep index, time, and timestep size are associated with the working state created by the history object.

Before

int index = solState->getIndex()+1;
double dt = constDT;
double time = index*dt;

After

int index = workingState->getIndex();
double dt = constDT;
double time = index*dt;
workingState->setTime(time);
workingState->setTimeStep(dt);

The working state is initialized by the history object and then updated with the time information for the tentative step.


Record and promote the working state

Promote the accepted working state. The step outcome is recorded on the working state, and the history object promotes it when the step completes.

Before

if ( std::isnan(Thyra::norm(*x_np1)) ) {
solState->setSolutionStatus(Tempus::Status::FAILED);
} else {
Thyra::V_V(x_n.ptr(), *x_np1);
solState->setIndex (index);
solState->setTime (time);
solState->setTimeStep(constDT);
solState->setSolutionStatus(Tempus::Status::PASSED);
}

After

if ( std::isnan(Thyra::norm(*x_np1)) ) {
workingState->setSolutionStatus(Tempus::Status::FAILED);
} else {
workingState->setSolutionStatus(Tempus::Status::PASSED);
}
solHistory->promoteWorkingState();

The step result is now associated with the working state, and the history object manages the promotion of that state to the current state.


How to compare the examples

A more detailed comparison can be made by diffing:

From the packages/tempus directory, a focused comparison of the main time-integration logic between these two examples can be generated locally in bash or zsh with:

git diff --no-index \
<(sed -n '69,149p' examples/03_Intro_SolutionState/03_Intro_SolutionState.cpp) \
<(sed -n '128,213p' examples/04_Adding_SolutionHistory/04_Adding_SolutionHistory.cpp)

This ignores leading header lines (e.g., #include statements and Doxygen comments) and trailing regression-testing lines.


← Previous Example | Current Example | Next Example →