-
Notifications
You must be signed in to change notification settings - Fork 107
SNES solver: Added a PID controller to update the timestep #3146
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
Changes from all commits
5f0720a
656dbdc
5a240c8
9276da0
210a6bc
e881832
1339804
687d21c
0c8ef87
fd6f189
8619876
c208639
d816d78
3be7177
751a22f
865c0ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -118,21 +118,32 @@ SNESSolver::SNESSolver(Options* opts) | |||||||||||
| maxits((*options)["max_nonlinear_iterations"] | ||||||||||||
| .doc("Maximum number of nonlinear iterations per SNES solve") | ||||||||||||
| .withDefault(50)), | ||||||||||||
| maxf((*options)["maxf"] | ||||||||||||
| .doc("Maximum number of function evaluations per SNES solve") | ||||||||||||
| .withDefault(10000)), | ||||||||||||
| lower_its((*options)["lower_its"] | ||||||||||||
| .doc("Iterations below which the next timestep is increased") | ||||||||||||
| .withDefault(static_cast<int>(maxits * 0.5))), | ||||||||||||
| upper_its((*options)["upper_its"] | ||||||||||||
| .doc("Iterations above which the next timestep is reduced") | ||||||||||||
| .withDefault(static_cast<int>(maxits * 0.8))), | ||||||||||||
| timestep_factor_on_failure((*options)["timestep_factor_on_failure"] | ||||||||||||
| .doc("Multiply timestep on convergence failure") | ||||||||||||
| .withDefault(0.5)), | ||||||||||||
| timestep_factor_on_upper_its((*options)["timestep_factor_on_upper_its"] | ||||||||||||
| .doc("Multiply timestep if iterations exceed upper_its") | ||||||||||||
| .withDefault(0.9)), | ||||||||||||
| timestep_factor_on_lower_its((*options)["timestep_factor_on_lower_its"] | ||||||||||||
| .doc("Multiply timestep if iterations are below lower_its") | ||||||||||||
| .withDefault(1.4)), | ||||||||||||
| .doc("Multiply timestep on convergence failure") | ||||||||||||
| .withDefault(0.5)), | ||||||||||||
| timestep_factor_on_upper_its( | ||||||||||||
| (*options)["timestep_factor_on_upper_its"] | ||||||||||||
| .doc("Multiply timestep if iterations exceed upper_its") | ||||||||||||
| .withDefault(0.9)), | ||||||||||||
| timestep_factor_on_lower_its( | ||||||||||||
| (*options)["timestep_factor_on_lower_its"] | ||||||||||||
| .doc("Multiply timestep if iterations are below lower_its") | ||||||||||||
| .withDefault(1.4)), | ||||||||||||
| pid_controller( | ||||||||||||
| (*options)["pid_controller"].doc("Use PID controller?").withDefault(false)), | ||||||||||||
| target_its((*options)["target_its"].doc("Target snes iterations").withDefault(7)), | ||||||||||||
| kP((*options)["kP"].doc("Proportional PID parameter").withDefault(0.7)), | ||||||||||||
| kI((*options)["kI"].doc("Integral PID parameter").withDefault(0.3)), | ||||||||||||
| kD((*options)["kD"].doc("Derivative PID parameter").withDefault(0.2)), | ||||||||||||
| diagnose( | ||||||||||||
| (*options)["diagnose"].doc("Print additional diagnostics").withDefault(false)), | ||||||||||||
| diagnose_failures((*options)["diagnose_failures"] | ||||||||||||
|
|
@@ -340,6 +351,7 @@ int SNESSolver::init() { | |||||||||||
|
|
||||||||||||
| // Set size of Matrix on each processor to nlocal x nlocal | ||||||||||||
| MatCreate(BoutComm::get(), &Jfd); | ||||||||||||
| MatSetOption(Jfd, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE); | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "MatSetOption" is directly included [misc-include-cleaner] MatSetOption(Jfd, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE);
^
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "PETSC_TRUE" is directly included [misc-include-cleaner] MatSetOption(Jfd, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE);
^ |
||||||||||||
| MatSetSizes(Jfd, nlocal, nlocal, PETSC_DETERMINE, PETSC_DETERMINE); | ||||||||||||
| MatSetFromOptions(Jfd); | ||||||||||||
| // Determine which row/columns of the matrix are locally owned | ||||||||||||
|
|
@@ -632,14 +644,21 @@ int SNESSolver::init() { | |||||||||||
| // Note: If the 'Amat' Jacobian is matrix free, SNESComputeJacobian | ||||||||||||
| // always updates its reference 'u' vector every nonlinear iteration | ||||||||||||
| SNESSetLagJacobian(snes, lag_jacobian); | ||||||||||||
| // Set Jacobian and preconditioner to persist across time steps | ||||||||||||
| SNESSetLagJacobianPersists(snes, PETSC_TRUE); | ||||||||||||
| SNESSetLagPreconditionerPersists(snes, PETSC_TRUE); | ||||||||||||
| if (pid_controller) { | ||||||||||||
| nl_its_prev = target_its; | ||||||||||||
| nl_its_prev2 = target_its; | ||||||||||||
| SNESSetLagJacobianPersists(snes, PETSC_FALSE); | ||||||||||||
| SNESSetLagPreconditionerPersists(snes, PETSC_FALSE); | ||||||||||||
| } else { | ||||||||||||
| // Set Jacobian and preconditioner to persist across time steps | ||||||||||||
| SNESSetLagJacobianPersists(snes, PETSC_TRUE); | ||||||||||||
| SNESSetLagPreconditionerPersists(snes, PETSC_TRUE); | ||||||||||||
| } | ||||||||||||
| SNESSetLagPreconditioner(snes, 1); // Rebuild when Jacobian is rebuilt | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Set tolerances | ||||||||||||
| SNESSetTolerances(snes, atol, rtol, stol, maxits, PETSC_DEFAULT); | ||||||||||||
| SNESSetTolerances(snes, atol, rtol, stol, maxits, maxf); | ||||||||||||
|
|
||||||||||||
| // Force SNES to take at least one nonlinear iteration. | ||||||||||||
| // This may prevent the solver from getting stuck in false steady state conditions | ||||||||||||
|
|
@@ -843,6 +862,10 @@ int SNESSolver::run() { | |||||||||||
| VecAXPBY(snes_x, -beta, (1. + beta), x1); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (pid_controller) { | ||||||||||||
| SNESSetLagJacobian(snes, lag_jacobian); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Run the solver | ||||||||||||
| PetscErrorCode ierr = SNESSolve(snes, nullptr, snes_x); | ||||||||||||
|
|
||||||||||||
|
|
@@ -900,6 +923,7 @@ int SNESSolver::run() { | |||||||||||
| updateColoring(); | ||||||||||||
| jacobian_pruned = false; // Reset flag. Will be set after pruning. | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (saved_jacobian_lag == 0) { | ||||||||||||
| // This triggers a Jacobian recalculation | ||||||||||||
| SNESGetLagJacobian(snes, &saved_jacobian_lag); | ||||||||||||
|
|
@@ -1033,44 +1057,61 @@ int SNESSolver::run() { | |||||||||||
| #endif // PETSC_VERSION_GE(3,20,0) | ||||||||||||
|
|
||||||||||||
| if (looping) { | ||||||||||||
| // Consider changing the timestep. | ||||||||||||
| // Note: The preconditioner depends on the timestep, | ||||||||||||
| // so if it is not recalculated the it will be less | ||||||||||||
| // effective. | ||||||||||||
| if ((nl_its <= lower_its) && (timestep < max_timestep) | ||||||||||||
| && (steps_since_snes_failure > 2)) { | ||||||||||||
| // Increase timestep slightly | ||||||||||||
| timestep *= timestep_factor_on_lower_its; | ||||||||||||
|
|
||||||||||||
| if (timestep > max_timestep) { | ||||||||||||
| timestep = max_timestep; | ||||||||||||
|
|
||||||||||||
| if (pid_controller) { | ||||||||||||
| // Changing the timestep. | ||||||||||||
| // Note: The preconditioner depends on the timestep, | ||||||||||||
| // so we recalculate the jacobian and the preconditioner | ||||||||||||
| // every time the timestep changes | ||||||||||||
|
|
||||||||||||
| timestep = pid(timestep, nl_its); | ||||||||||||
|
|
||||||||||||
| // NOTE(malamast): Do we really need this? | ||||||||||||
| // Recompute Jacobian (for now) | ||||||||||||
| if (saved_jacobian_lag == 0) { | ||||||||||||
| SNESGetLagJacobian(snes, &saved_jacobian_lag); | ||||||||||||
| SNESSetLagJacobian(snes, 1); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Note: Setting the SNESJacobianFn to NULL retains | ||||||||||||
| // previously set evaluation function. | ||||||||||||
| // | ||||||||||||
| // The SNES Jacobian is a combination of the RHS Jacobian | ||||||||||||
| // and a factor involving the timestep. | ||||||||||||
| // Depends on equation_form | ||||||||||||
| // -> Probably call SNESSetJacobian(snes, Jfd, Jfd, NULL, fdcoloring); | ||||||||||||
| } else { | ||||||||||||
|
|
||||||||||||
| // Consider changing the timestep. | ||||||||||||
| // Note: The preconditioner depends on the timestep, | ||||||||||||
| // so if it is not recalculated the it will be less | ||||||||||||
| // effective. | ||||||||||||
| if ((nl_its <= lower_its) && (timestep < max_timestep) | ||||||||||||
| && (steps_since_snes_failure > 2)) { | ||||||||||||
| // Increase timestep slightly | ||||||||||||
| timestep *= timestep_factor_on_lower_its; | ||||||||||||
|
|
||||||||||||
| timestep = std::min(timestep, max_timestep); | ||||||||||||
|
|
||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "std::min" is directly included [misc-include-cleaner] ^ |
||||||||||||
| // Note: Setting the SNESJacobianFn to NULL retains | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: use src/solver/impls/snes/snes.cxx:6: - #include <bout/boutcomm.hxx>
+ #include <algorithm>
+ #include <bout/boutcomm.hxx>
Suggested change
|
||||||||||||
| // previously set evaluation function. | ||||||||||||
| // | ||||||||||||
| // The SNES Jacobian is a combination of the RHS Jacobian | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: use src/solver/impls/snes/snes.cxx:6: - #include <bout/boutcomm.hxx>
+ #include <algorithm>
+ #include <bout/boutcomm.hxx>
Suggested change
|
||||||||||||
| // and a factor involving the timestep. | ||||||||||||
| // Depends on equation_form | ||||||||||||
| // -> Probably call SNESSetJacobian(snes, Jfd, Jfd, NULL, fdcoloring); | ||||||||||||
|
|
||||||||||||
| if (static_cast<BoutReal>(lin_its) / nl_its > 4) { | ||||||||||||
| // Recompute Jacobian (for now) | ||||||||||||
| if (saved_jacobian_lag == 0) { | ||||||||||||
| SNESGetLagJacobian(snes, &saved_jacobian_lag); | ||||||||||||
| SNESSetLagJacobian(snes, 1); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (static_cast<BoutReal>(lin_its) / nl_its > 4) { | ||||||||||||
| // Recompute Jacobian (for now) | ||||||||||||
| } else if (nl_its >= upper_its) { | ||||||||||||
| // Reduce timestep slightly | ||||||||||||
| timestep *= timestep_factor_on_upper_its; | ||||||||||||
|
|
||||||||||||
| // Recompute Jacobian | ||||||||||||
| if (saved_jacobian_lag == 0) { | ||||||||||||
| SNESGetLagJacobian(snes, &saved_jacobian_lag); | ||||||||||||
| SNESSetLagJacobian(snes, 1); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| } else if (nl_its >= upper_its) { | ||||||||||||
| // Reduce timestep slightly | ||||||||||||
| timestep *= timestep_factor_on_upper_its; | ||||||||||||
|
|
||||||||||||
| // Recompute Jacobian | ||||||||||||
| if (saved_jacobian_lag == 0) { | ||||||||||||
| SNESGetLagJacobian(snes, &saved_jacobian_lag); | ||||||||||||
| SNESSetLagJacobian(snes, 1); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| snes_failures = 0; | ||||||||||||
|
|
@@ -1360,7 +1401,10 @@ void SNESSolver::updateColoring() { | |||||||||||
| // Re-calculate the coloring | ||||||||||||
| MatColoring coloring = NULL; | ||||||||||||
| MatColoringCreate(Jfd, &coloring); | ||||||||||||
| MatColoringSetType(coloring, MATCOLORINGSL); | ||||||||||||
| // MatColoringSetType(coloring, MATCOLORINGSL); // Serial algorithm. Better for smale-to-medium size problems. | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "MatColoringCreate" is directly included [misc-include-cleaner] NULL;
^ |
||||||||||||
| MatColoringSetType( | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "MatColoringSetType" is directly included [misc-include-cleaner] lems.
^
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "MatColoring" is directly included [misc-include-cleaner] oring
^ |
||||||||||||
| coloring, MATCOLORINGGREEDY); // Parallel algorith. Better for large parallel runs | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "MATCOLORINGGREEDY" is directly included [misc-include-cleaner] Type(
^
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "MatColoringCreate" is directly included [misc-include-cleaner] NULL;
^
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "NULL" is directly included [misc-include-cleaner] src/solver/impls/snes/snes.cxx:1: + #include <cstddef>
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "MATCOLORINGGREEDY" is directly included [misc-include-cleaner] lems.
^
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "MatColoringSetType" is directly included [misc-include-cleaner] lems.
^ |
||||||||||||
| // MatColoringSetType(coloring, MATCOLORINGJP); // This didn't work | ||||||||||||
| MatColoringSetFromOptions(coloring); | ||||||||||||
|
|
||||||||||||
| // Calculate new index sets | ||||||||||||
|
|
@@ -1386,4 +1430,25 @@ void SNESSolver::updateColoring() { | |||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| BoutReal SNESSolver::pid(BoutReal timestep, int nl_its) { | ||||||||||||
|
|
||||||||||||
| /* ---------- multiplicative PID factors ---------- */ | ||||||||||||
| const BoutReal facP = std::pow(double(target_its) / double(nl_its), kP); | ||||||||||||
| const BoutReal facI = std::pow(double(nl_its_prev) / double(nl_its), kI); | ||||||||||||
| const BoutReal facD = std::pow(double(nl_its_prev) * double(nl_its_prev) | ||||||||||||
| / double(nl_its) / double(nl_its_prev2), | ||||||||||||
| kD); | ||||||||||||
|
|
||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: variable 'facD' of type 'BoutReal' (aka 'double') can be declared 'const' [misc-const-correctness]
Suggested change
|
||||||||||||
| // clamp groth factor to avoid huge changes | ||||||||||||
| const BoutReal fac = std::clamp(facP * facI * facD, 0.2, 5.0); | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "std::clamp" is directly included [misc-include-cleaner] anges
^ |
||||||||||||
|
|
||||||||||||
| /* ---------- update timestep and history ---------- */ | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: statement should be inside braces [readability-braces-around-statements]
Suggested change
|
||||||||||||
| const BoutReal dt_new = std::min(timestep * fac, max_timestep); | ||||||||||||
|
|
||||||||||||
| nl_its_prev2 = nl_its_prev; | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: use
Suggested change
|
||||||||||||
| nl_its_prev = nl_its; | ||||||||||||
|
|
||||||||||||
| return dt_new; | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: use
Suggested change
|
||||||||||||
| } | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: redundant explicit casting to the same type 'int' as the sub-expression, remove this casting [readability-redundant-casting]
Suggested change
src/solver/impls/snes/snes.cxx:1463: - ts);
+ ts;Additional contextsrc/solver/impls/snes/snes.cxx:1437: source type originates from referencing this parameter }
^ |
||||||||||||
|
|
||||||||||||
| #endif // BOUT_HAS_PETSC | ||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: no header providing "MAT_KEEP_NONZERO_PATTERN" is directly included [misc-include-cleaner]
MatSetOption(Jfd, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE); ^