Skip to content
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

Add matrix coloring to beuler solver (and fix imexbdf2) #2454

Merged
merged 33 commits into from
Feb 9, 2022

Conversation

bendudson
Copy link
Contributor

Enables large reductions in the number of iterations needed to calculate the Jacobian elements. Having the Jacobian then enables good preconditioning.

Simple conduction example, nout=5 and timestep=10:

PVODE:

1.000e+01        644       1.25e-01    74.9    0.0    0.7   13.4   10.9
2.000e+01        161       4.41e-02    50.9    0.0    0.4   29.4   19.2

beuler, SNESType = anderson

1.000e+01        962       2.13e-01    65.8    0.0    0.7    8.2   25.4
2.000e+01        347       8.20e-02    61.3    0.0    0.6   14.0   24.0

beuler, SNESType = newtonls, matrix free (no Jacobian)

1.000e+01       3389       5.68e-01    89.2    0.0    0.8    3.3    6.6
2.000e+01       2325       3.55e-01    89.1    0.0    0.8    3.3    6.8

beuler, SNESType = newtonls, with Jacobian, no coloring

1.000e+01        833       1.53e-01    78.8    0.0    0.7   12.1    8.4
2.000e+01        521       9.81e-02    76.6    0.0    0.8   12.3   10.4

beuler, SNESType = newtonls, with Jacobian coloring

1.000e+01         85       3.78e-02    33.3    0.0    0.3   46.5   19.9
2.000e+01         57       3.16e-02    26.4    0.0    0.2   48.9   24.6

Also includes some reformatting and drive-by fixes for things flagged by Apple Clang. These are in separate commits (mostly) for reviewing.

Solvers can pass a flag to say whether the function can be linearised
(e.g. inside inner linear solve, or Jacobian calculation).
Physics models don't have to use the argument: rhs functions with
a single (time) argument continue to work.
Should use const reference to avoid copying index
Deprecated, flagged as warning by Apple Clang
Enables large reductions in the number of iterations needed to
calculate the Jacobian elements. Having the Jacobian then enables
good preconditioning.

Simple conduction example, nout=5 and timestep=10:

PVODE:
```
1.000e+01        644       1.25e-01    74.9    0.0    0.7   13.4   10.9
2.000e+01        161       4.41e-02    50.9    0.0    0.4   29.4   19.2
```

beuler, SNESType = anderson
```
1.000e+01        962       2.13e-01    65.8    0.0    0.7    8.2   25.4
2.000e+01        347       8.20e-02    61.3    0.0    0.6   14.0   24.0
```

beuler, SNESType = newtonls, matrix free (no Jacobian)
```
1.000e+01       3389       5.68e-01    89.2    0.0    0.8    3.3    6.6
2.000e+01       2325       3.55e-01    89.1    0.0    0.8    3.3    6.8
```

beuler, SNESType = newtonls, with Jacobian, no coloring
```
1.000e+01        833       1.53e-01    78.8    0.0    0.7   12.1    8.4
2.000e+01        521       9.81e-02    76.6    0.0    0.8   12.3   10.4
```

beuler, SNESType = newtonls, with Jacobian coloring
```
1.000e+01         85       3.78e-02    33.3    0.0    0.3   46.5   19.9
2.000e+01         57       3.16e-02    26.4    0.0    0.2   48.9   24.6
```
Was destroying the iscoloring object before setup, and then
reverting to brute-force calculation of the Jacobian.
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 151. Check the log or trigger a new build to see more.


// Redundent definition because < C++17
constexpr int IMEXBDF2::MAX_SUPPORTED_ORDER;

IMEXBDF2::IMEXBDF2(Options *opt)
IMEXBDF2::IMEXBDF2(Options* opt)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: constructor does not initialize these fields: maxOrder, out_timestep, nsteps, timestep, ninternal, mxstep, adaptive, nadapt, mxstepAdapt, scaleCushUp, scaleCushDown, adaptRtol, dtMin, dtMax, dtMinFatal, dtImp, nlocal, neq, implicit_gamma, implicit_curtime, predictor, diagnose, verbose, linear_fails, nonlinear_fails, have_constraints, fdcoloring [cppcoreguidelines-pro-type-member-init]

IMEXBDF2::IMEXBDF2(Options* opt)
^

src/solver/impls/imex-bdf2/imex-bdf2.cxx Show resolved Hide resolved
src/solver/impls/imex-bdf2/imex-bdf2.cxx Show resolved Hide resolved
src/solver/impls/imex-bdf2/imex-bdf2.cxx Outdated Show resolved Hide resolved
src/solver/impls/imex-bdf2/imex-bdf2.cxx Outdated Show resolved Hide resolved
o_nnz[localIndex+i] += (n3d + n2d);
for (int i = 0; i < n3d; i++) {
d_nnz[localIndex + i] -= (n3d + n2d);
o_nnz[localIndex + i] += (n3d + n2d);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

o_nnz[localIndex + i] += (n3d + n2d);
^

o_nnz[localIndex+i] += (n3d + n2d);
for (int i = 0; i < n2d + n3d; i++) {
// d_nnz[localIndex+i] -= (n3d + n2d);
o_nnz[localIndex + i] += (n3d + n2d);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

o_nnz[localIndex + i] += (n3d + n2d);
^

o_nnz[localIndex+i] += (n3d + n2d);
for (int i = 0; i < n3d; i++) {
// d_nnz[localIndex+i] -= (n3d + n2d);
o_nnz[localIndex + i] += (n3d + n2d);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

o_nnz[localIndex + i] += (n3d + n2d);
^

o_nnz[localIndex+i] += (n3d + n2d);
for (int i = 0; i < n2d + n3d; i++) {
// d_nnz[localIndex+i] -= (n3d + n2d);
o_nnz[localIndex + i] += (n3d + n2d);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

o_nnz[localIndex + i] += (n3d + n2d);
^

o_nnz[localIndex+i] += (n3d + n2d);
for (int i = 0; i < n3d; i++) {
// d_nnz[localIndex+i] -= (n3d + n2d);
o_nnz[localIndex + i] += (n3d + n2d);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

o_nnz[localIndex + i] += (n3d + n2d);
^

Describe all the options, and how to use newtonls with Jacobian
coloring.
- Defaults to using coloring to calculate a Jacobian
- Iterations and lag jacobian set to values that work for
  SD1D tests so far.
- Diagnostic outputs now include number of linear iterations
  and number of failures.
Reflect changes in defaults, and some notes on when these
are likely to fail.
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 128. Check the log or trigger a new build to see more.

for(int i=0;i<n2d+n3d;i++) {
o_nnz[localIndex+i] -= (n3d + n2d);
for (int i = 0; i < n2d + n3d; i++) {
o_nnz[localIndex + i] -= (n3d + n2d);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

o_nnz[localIndex + i] -= (n3d + n2d);
^

for(int i=0;i<n3d;i++) {
o_nnz[localIndex+i] -= (n3d + n2d);
for (int i = 0; i < n3d; i++) {
o_nnz[localIndex + i] -= (n3d + n2d);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

o_nnz[localIndex + i] -= (n3d + n2d);
^

for(int i=0;i<n2d+n3d;i++) {
o_nnz[localIndex+i] -= (n3d + n2d);
for (int i = 0; i < n2d + n3d; i++) {
o_nnz[localIndex + i] -= (n3d + n2d);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

o_nnz[localIndex + i] -= (n3d + n2d);
^

for(int i=0;i<n3d;i++) {
o_nnz[localIndex+i] -= (n3d + n2d);
for (int i = 0; i < n3d; i++) {
o_nnz[localIndex + i] -= (n3d + n2d);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

o_nnz[localIndex + i] -= (n3d + n2d);
^

const int xoffset[5] = {0,-1, 1, 0, 0};
const int yoffset[5] = {0, 0, 0,-1, 1};

const int xoffset[5] = {0, -1, 1, 0, 0};
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]

const int xoffset[5] = {0, -1, 1, 0, 0};
      ^

Copy link
Member

Choose a reason for hiding this comment

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

Not touched in this PR, but this is a good suggestion we should do at some point

// An error occurred. If adaptive, reduce timestep
if(!adaptive)
if (!adaptive)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: statement should be inside braces [readability-braces-around-statements]

if (!adaptive)
              ^
               {

// Now we can calculate the error and decide what we want to do
if (checkingErr) {
// Now we want to find the actual (abs) error
BoutReal errTot[3] = {0, 0, 0};
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]

BoutReal errTot[3] = {0, 0, 0};
^

if (checkingErr) {
// Now we want to find the actual (abs) error
BoutReal errTot[3] = {0, 0, 0};
BoutReal errGlobTot[3] = {0, 0, 0};
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]

BoutReal errGlobTot[3] = {0, 0, 0};
^

//Increment order if we're not at the maximum requested
if(order<maxOrder) order++;
// Increment order if we're not at the maximum requested
if (order < maxOrder)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: statement should be inside braces [readability-braces-around-statements]

if (order < maxOrder)
                     ^
                      {


iteration++; // Advance iteration number

/// Call the monitor function

if(call_monitors(simtime, s, nsteps)) {
if (call_monitors(simtime, s, nsteps)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: implicit conversion int -> bool [readability-implicit-bool-conversion]

if (call_monitors(simtime, s, nsteps)) {
    ^
                                      != 0

Clang tidy suggestions
If timestep falls below this threshold, reset the Jacobian and
preconditioner, and try to take a large timestep.

The idea is that sometimes the solver gets stuck in a local
minimum, and the observation that sometimes stopping the
simulation and restarting can help.
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 103. Check the log or trigger a new build to see more.

@@ -1152,97 +1176,102 @@ PetscErrorCode IMEXBDF2::solve_implicit(BoutReal curtime, BoutReal gamma) {
implicit_gamma = gamma;

// Set initial guess at the solution
BoutReal *xdata;
BoutReal* xdata;
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: variable xdata is not initialized [cppcoreguidelines-init-variables]

BoutReal* xdata;
          ^
                = nullptr

for(int i=0;i<nlocal;i++) {
xdata[i] = uV[0][i]; // Use previous solution
for (int i = 0; i < nlocal; i++) {
xdata[i] = uV[0][i]; // Use previous solution
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

xdata[i] = uV[0][i]; // Use previous solution
^

for(int i=0;i<nlocal;i++) {
xdata[i] = 2.*uV[0][i] - uV[1][i];
for (int i = 0; i < nlocal; i++) {
xdata[i] = 2. * uV[0][i] - uV[1][i];
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

xdata[i] = 2. * uV[0][i] - uV[1][i];
^

for(int i=0;i<nlocal;i++) {
xdata[i] = 3.*uV[0][i] - 3.*uV[1][i] + uV[2][i];
for (int i = 0; i < nlocal; i++) {
xdata[i] = 3. * uV[0][i] - 3. * uV[1][i] + uV[2][i];
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

xdata[i] = 3. * uV[0][i] - 3. * uV[1][i] + uV[2][i];
^

for(int i=0;i<nlocal;i++) {
xdata[i] = rhs[i]; // If G = 0
for (int i = 0; i < nlocal; i++) {
xdata[i] = rhs[i]; // If G = 0
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

xdata[i] = rhs[i]; // If G = 0
^

op.run(jx, jy, jz, u); ++u;
if (mesh->firstX() && !mesh->periodicX) {
for (int jx = 0; jx < mesh->xstart; ++jx)
for (int jy = mesh->ystart; jy <= mesh->yend; ++jy)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: statement should be inside braces [readability-braces-around-statements]

for (int jy = mesh->ystart; jy <= mesh->yend; ++jy)
                                                   ^
                                                    {

src/solver/impls/imex-bdf2/imex-bdf2.cxx Show resolved Hide resolved
for(int jz=0; jz < mesh->LocalNz; ++jz) {
op.run(jx, jy, jz, u); ++u;
if (mesh->lastX() && !mesh->periodicX) {
for (int jx = mesh->xend + 1; jx < mesh->LocalNx; ++jx)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: statement should be inside braces [readability-braces-around-statements]

for (int jx = mesh->xend + 1; jx < mesh->LocalNx; ++jx)
                                                       ^
                                                        {

op.run(jx, jy, jz, u); ++u;
if (mesh->lastX() && !mesh->periodicX) {
for (int jx = mesh->xend + 1; jx < mesh->LocalNx; ++jx)
for (int jy = mesh->ystart; jy <= mesh->yend; ++jy)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: statement should be inside braces [readability-braces-around-statements]

for (int jy = mesh->ystart; jy <= mesh->yend; ++jy)
                                                   ^
                                                    {

src/solver/impls/imex-bdf2/imex-bdf2.cxx Show resolved Hide resolved
- If resetting doesn't work once, quit rather than get stuck
  in an infinite loop
- Turn off the predictor when resetting. The predictor seems to
  make convergence slower when nearly in steady state.
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 78. Check the log or trigger a new build to see more.

for(int jz=0; jz < mesh->LocalNz; ++jz) {
op.run(*xi, jy, jz, u); ++u;
for (RangeIterator xi = mesh->iterateBndryLowerY(); !xi.isDone(); ++xi) {
for (int jy = 0; jy < mesh->ystart; ++jy)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: statement should be inside braces [readability-braces-around-statements]

for (int jy = 0; jy < mesh->ystart; ++jy)
                                         ^
                                          {

src/solver/impls/imex-bdf2/imex-bdf2.cxx Show resolved Hide resolved
for(int jz=0; jz < mesh->LocalNz; ++jz) {
op.run(*xi, jy, jz, u); ++u;
for (RangeIterator xi = mesh->iterateBndryUpperY(); !xi.isDone(); ++xi) {
for (int jy = mesh->yend + 1; jy < mesh->LocalNy; ++jy)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: statement should be inside braces [readability-braces-around-statements]

for (int jy = mesh->yend + 1; jy < mesh->LocalNy; ++jy)
                                                       ^
                                                        {

for (int jy = mesh->yend + 1; jy < mesh->LocalNy; ++jy)
for (int jz = 0; jz < mesh->LocalNz; ++jz) {
op.run(*xi, jy, jz, u);
++u;
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]

++u;
^

for(int jy=mesh->ystart; jy <= mesh->yend; ++jy)
for(int jz=0; jz < mesh->LocalNz; ++jz) {
op.run(jx, jy, jz, u); ++u;
for (int jx = mesh->xstart; jx <= mesh->xend; ++jx)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: statement should be inside braces [readability-braces-around-statements]

for (int jx = mesh->xstart; jx <= mesh->xend; ++jx)
                                                   ^
                                                    {

src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
MatSetFromOptions(Jmf);

PetscInt *d_nnz, *o_nnz;
PetscMalloc((localN) * sizeof(PetscInt), &d_nnz);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

PetscMalloc((localN) * sizeof(PetscInt), &d_nnz);
      ^
/usr/lib/petsc/include/petscsys.h:453:99: note: expanded from macro 'PetscMalloc'
#define PetscMalloc(a,b)  ((*PetscTrMalloc)((a),PETSC_FALSE,__LINE__,PETSC_FUNCTION_NAME,__FILE__,(void**)(b)))
                                                                                                  ^

Copy link
Member

Choose a reason for hiding this comment

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

The docs suggest using PetscNew or PetscMalloc1 instead.

Could we even use std::vector<PetscInt> d_nnz(localN) here instead? I can't quite get my head around all the alignment business and if it's required, but I think this should be fine? This would eliminate the calls to PetscFree, and should hopefully avoid all the warnings about pointer arithmetic

@ZedThree
Copy link
Member

I think we probably need to turn off cppcoreguidelines-pro-bounds-pointer-arithmetic, as it appears we use pointer arithmetic too much

Missing on for loops in imex-bdf2 code
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 53. Check the log or trigger a new build to see more.

src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
@dschwoerer
Copy link
Contributor

dschwoerer commented Oct 22, 2021 via email

Thanks to @dschworer suggestion.

[skip ci]
@dschwoerer
Copy link
Contributor

For me the old defaults give better results 😞
Would it make sense to have a meta-option, to switch between different defaults? That would allow users to try different things, without having to worry about the various options initially ...

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 28. Check the log or trigger a new build to see more.

src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
// Mark non-zero entries

// Offsets for a 5-point pattern
const int xoffset[5] = {0, -1, 1, 0, 0};
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]

const int xoffset[5] = {0, -1, 1, 0, 0};
      ^

Copy link
Member

Choose a reason for hiding this comment

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

As this is new code, I think it's worth implementing this suggestion:

Suggested change
const int xoffset[5] = {0, -1, 1, 0, 0};
constexpr std::array<int, 5> xoffset = {0, -1, 1, 0, 0};


// Offsets for a 5-point pattern
const int xoffset[5] = {0, -1, 1, 0, 0};
const int yoffset[5] = {0, 0, 0, -1, 1};
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]

const int yoffset[5] = {0, 0, 0, -1, 1};
      ^

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const int yoffset[5] = {0, 0, 0, -1, 1};
constexpr std::array<int, 5> yoffset = {0, 0, 0, -1, 1};

src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
If a step which would have ended at an output time fails,
the looping variable should be reset to true so that the
retry occurs.

Thanks to Mike Kryjak for the report
Sets the linear solver and preconditioner to use
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
@@ -1246,13 +1246,13 @@ int Solver::run_rhs(BoutReal t) {

save_vars(tmp.begin()); // Copy variables into tmp
pre_rhs(t);
status = model->runConvective(t);
status = model->runConvective(t, linear);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: Value stored to status is never read [clang-analyzer-deadcode.DeadStores]

status = model->runConvective(t, linear);
    ^
src/solver/solver.cxx:1249:5: note: Value stored to 'status' is never read

beuler sometimes appears to reach a steady state, with timesteps
continuously increasing, and zero nonlinear iterations e.g.

```
Time: 2189781.780775979, timestep: 9781.780775979214, nl iter: 0, lin iter: 1
Time: 2190000.0, timestep: 10759.958853577136, nl iter: 0, lin iter: 1
2.190e+06         31       1.94e+00    94.9    0.0    0.0    3.0    2.0
Time: 2200000.0, timestep: 10759.958853577136, nl iter: 0, lin iter: 1
2.200e+06         16       1.05e+00    91.0    0.0    0.0    6.3    2.7
Time: 2210000.0, timestep: 10759.958853577136, nl iter: 0, lin iter: 1
2.210e+06         16       1.08e+00    91.9    0.0    0.0    5.5    2.6
Time: 2220000.0, timestep: 10759.958853577136, nl iter: 0, lin iter: 1
```

Restarting from this state however shows that it is not in steady
state and even doesn't converge.

To try to prevent this, force SNES to take at least one iteration, add
more checks and reporting.
Will sometimes hit a state where snes stops after zero iterations
(due to stol tolerance), and continues "converging" until the end
of the simulation. Taking a small Euler step seems to help.
Add ability to change line search type.

When SNES fails to converge, print diagnostic information on the
fields and their time derivatives.
ZedThree
ZedThree previously approved these changes Nov 22, 2021
Copy link
Member

@ZedThree ZedThree left a comment

Choose a reason for hiding this comment

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

LGTM. There's a few bits that could be polished perhaps. Lots of noise from clang-tidy because PETSc.

There's a fair bit of repeated code between IMEX-BDF2 and SNES for the colouring -- is it possible to pull this out into a shared helper function/class?

src/solver/impls/imex-bdf2/imex-bdf2.cxx Show resolved Hide resolved
const int xoffset[5] = {0,-1, 1, 0, 0};
const int yoffset[5] = {0, 0, 0,-1, 1};

const int xoffset[5] = {0, -1, 1, 0, 0};
Copy link
Member

Choose a reason for hiding this comment

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

Not touched in this PR, but this is a good suggestion we should do at some point

src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
MatSetFromOptions(Jmf);

PetscInt *d_nnz, *o_nnz;
PetscMalloc((localN) * sizeof(PetscInt), &d_nnz);
Copy link
Member

Choose a reason for hiding this comment

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

The docs suggest using PetscNew or PetscMalloc1 instead.

Could we even use std::vector<PetscInt> d_nnz(localN) here instead? I can't quite get my head around all the alignment business and if it's required, but I think this should be fine? This would eliminate the calls to PetscFree, and should hopefully avoid all the warnings about pointer arithmetic

} else {
// Only 3D fields
for (int i = 0; i < n3d; i++) {
d_nnz[localIndex + i] -= (n3d + n2d);
Copy link
Member

Choose a reason for hiding this comment

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

Just flagging this to be double-checked: is the rhs supposed to match the loop limit? i.e. should this be

Suggested change
d_nnz[localIndex + i] -= (n3d + n2d);
d_nnz[localIndex + i] -= n3d;

Comment on lines 219 to 228
if (z == 0) {
// All 2D and 3D fields
for (int i = 0; i < n2d + n3d; i++) {
d_nnz[localIndex + i] -= (n3d + n2d);
}
} else {
// Only 3D fields
for (int i = 0; i < n3d; i++) {
d_nnz[localIndex + i] -= (n3d + n2d);
}
Copy link
Member

Choose a reason for hiding this comment

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

Assuming the loop body is supposed to be identical between the branches, here's a more concise way of writing this that avoids the repetition in the loop body:

Suggested change
if (z == 0) {
// All 2D and 3D fields
for (int i = 0; i < n2d + n3d; i++) {
d_nnz[localIndex + i] -= (n3d + n2d);
}
} else {
// Only 3D fields
for (int i = 0; i < n3d; i++) {
d_nnz[localIndex + i] -= (n3d + n2d);
}
const auto num_fields = (z == 0) ? n2d + n3d : n3d;
for (int i = 0; i < num_fields; i++) {
d_nnz[localIndex + i] -= (n3d + n2d);
}

Copy link
Member

Choose a reason for hiding this comment

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

I also wonder if these loop bodies could be wrapped up into functions and reused? Might cut down on this section a fair bit

// Mark non-zero entries

// Offsets for a 5-point pattern
const int xoffset[5] = {0, -1, 1, 0, 0};
Copy link
Member

Choose a reason for hiding this comment

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

As this is new code, I think it's worth implementing this suggestion:

Suggested change
const int xoffset[5] = {0, -1, 1, 0, 0};
constexpr std::array<int, 5> xoffset = {0, -1, 1, 0, 0};


// Offsets for a 5-point pattern
const int xoffset[5] = {0, -1, 1, 0, 0};
const int yoffset[5] = {0, 0, 0, -1, 1};
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const int yoffset[5] = {0, 0, 0, -1, 1};
constexpr std::array<int, 5> yoffset = {0, 0, 0, -1, 1};

PetscInt row = ind0 + i;

// Loop through each point in the 5-point stencil
for (int c = 0; c < 5; c++) {
Copy link
Member

Choose a reason for hiding this comment

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

One day C++ will have zip!

/// @param[out] f The vector for the result f(x)
/// @param[in] linear Specifies that the SNES solver is in a linear (KSP) inner loop,
/// so the operator should be linearised if possible
PetscErrorCode snes_function(Vec x, Vec f, bool linear); ///< Nonlinear function
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
PetscErrorCode snes_function(Vec x, Vec f, bool linear); ///< Nonlinear function
PetscErrorCode snes_function(Vec x, Vec f, bool linear);

@johnomotani
Copy link
Contributor

Sorry, didn't have time to look through the code yet (will try to get to it).

One thought though - this might be something for a separate PR, but I don't think these solvers are using the Petsclib features for setting options that were added in #1795 (like for example LaplaceXY and LaplacePetsc3dAmg do). The PETSc options feature was backported to 4.4 too. Petsclib might need a small update to support setting options for SNES (maybe an extra method - I haven't checked or thought about it...) but using it has the advantage that all PETSc options are available without having to add a BOUT++ option and call the setter function - including new options in future PETSc versions!

If this is a good thing to do, it might be good to add sooner rather than later, since it would change the input file structure - i.e. use a beuler:petsc subsection instead of options in beuler.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
Fails to compile on github due to using petsc 3.7.7
equation_form switches between:
- Pseudo-transient (like UEDGE)
- A rearranged backward Euler
- The original backward Euler
Copy link
Member

@ZedThree ZedThree left a comment

Choose a reason for hiding this comment

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

LGTM, but a couple of things to double check, and a small number of things to fix

src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved

// Only 3D fields
for (int i = 0; i < n3d; i++) {
// d_nnz[localIndex+i] -= (n3d + n2d);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// d_nnz[localIndex+i] -= (n3d + n2d);

localIndex = ROUND(index(x, mesh->yend, 0));
// All 2D and 3D fields
for (int i = 0; i < n2d + n3d; i++) {
// d_nnz[localIndex+i] -= (n3d + n2d);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// d_nnz[localIndex+i] -= (n3d + n2d);


// Only 3D fields
for (int i = 0; i < n3d; i++) {
// d_nnz[localIndex+i] -= (n3d + n2d);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// d_nnz[localIndex+i] -= (n3d + n2d);

Comment on lines 219 to 228
if (z == 0) {
// All 2D and 3D fields
for (int i = 0; i < n2d + n3d; i++) {
d_nnz[localIndex + i] -= (n3d + n2d);
}
} else {
// Only 3D fields
for (int i = 0; i < n3d; i++) {
d_nnz[localIndex + i] -= (n3d + n2d);
}
Copy link
Member

Choose a reason for hiding this comment

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

I also wonder if these loop bodies could be wrapped up into functions and reused? Might cut down on this section a fair bit

for (int j = 0; j < n2d; j++) {
PetscInt col = ind2 + j;

MatSetValues(Jmf, 1, &row, 1, &col, &val, INSERT_VALUES);
Copy link
Member

Choose a reason for hiding this comment

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

Is it worth looking at using our Petsc Matrix wrapper for this new code?


equation_form = (*options)["equation_form"]
Copy link
Member

Choose a reason for hiding this comment

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

Could equation_form be a BOUT_ENUM_CLASS? Then users could use the names directly, rather than integers

bendudson and others added 2 commits December 14, 2021 15:19
In y boundaries the X index can be out of the domain,
leading to negative indices. This caused out of bounds memory access,
and a lot of slowdown in the Jacobian coloring setup.

Also added some progress output
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
src/solver/impls/snes/snes.cxx Outdated Show resolved Hide resolved
bendudson and others added 8 commits December 14, 2021 17:48
Also add some notes on preconditioner options
Co-authored-by: Peter Hill <zed.three@gmail.com>
Limits how large the timestep can be made, to try and prevent
repeated increases and failures
* next: (37 commits)
  Merge in Solver and PhysicsModel changes from next
  SNES solver merges
  Fix contributor's name
  Update release date
  Update changelog
  Fix ambiguous visit call
  Revert SONAME/SOVERSION to 4.4.0
  Fix test-bout-override-default-option for cmake
  Update DOI and release date for 4.4.1
  Update changelog for 4.4.1
  Update translation files for 4.4.1
  Bump version to 4.4.1
  Enable setting SNES solver PETSc options from input file
  Try to consolidate some loops/branches in SNESSolver
  Use `BOUT_ENUM_CLASS` for `SNESSolver::equation_form`
  Use `std::vector/array` instead of C style arrays
  Remove `__FUNCT__` macros for PETSc callbacks
  Update backport of beuler/snes solver
  Fix typo in docs
  Remove IDL section and recommend xBOUT for analysis
  ...
@ZedThree
Copy link
Member

ZedThree commented Feb 8, 2022

The black check failed because we're running it on both pull requests and pushes. There's a few CI things to clean up, I'll do so in another PR

@ZedThree ZedThree merged commit d746cda into next Feb 9, 2022
@ZedThree ZedThree deleted the beuler-jacobian-color branch February 9, 2022 11:09
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.

None yet

4 participants